Payments.lk

Developers / API reference

The API, endpoint by endpoint.

Every endpoint with its parameters, errors and a request you can paste, the objects it returns, and the webhooks it sends. Generated from the same schemas the API validates with, so what is here is what the API accepts. New to it? Start with the developer guide.

Introduction

The API is JSON over HTTPS at https://api.payments.lk. Send bodies as JSON with Content-Type: application/json; every answer, errors included, is JSON. Test and live keys use the same address: the key decides the mode.

The whole API is also described as an OpenAPI 3.1 document, which the API serves itself, without a key, to any origin. Point Postman, an SDK generator or an AI agent at it. The MCP endpoint for AI agents, at /mcp, is not part of this reference; it has its own agent guide.

Keys and authentication

Send your key as a bearer token: Authorization: Bearer sk_test_.... Keys are in your dashboard under Developers. A missing, malformed, revoked or expired key answers 401; a key that may not do what you asked answers 403.

KeyWhat it may do
sk_test_, sk_live_Secret key. Every endpoint here. Keep it on your server; never ship it in a web page or an app.
pk_test_, pk_live_Publishable key. Only POST /v1/checkouts; everything else answers 403. The API answers cross-origin requests only for Payments.lk's own sites, so JavaScript on your site cannot call it with this key either. Create checkouts on your server with the secret key.
ak_test_, ak_live_Agent key, for an AI agent at the MCP endpoint only. Every endpoint here answers 403.

Test and live

Every object carries a mode, test or live, taken from the key that made it. A test key reads and writes test objects only, and a live key live objects only, so changing the key is the whole switch.

Test checkouts run on Payable’s sandbox with the card networks’ test cards, once you have submitted your application; no money moves. The test cards and the outcome each expiry date gives are on the developers page. Live keys take payments once your account is activated; until then a live checkout, payment link or card charge answers 409.

Idempotency

Every write marked Idempotency-Key required takes an Idempotency-Key header of 8 to 255 letters, digits, dashes, underscores, colons or dots. Make it from your own record, such as order-8891, so a retry after a timeout sends the same key.

A key is remembered for 24 hours, separately for test and live keys. Within that time the same key with the same body gets the first answer back, with the header Idempotent-Replayed: true, and nothing is made twice. The same key with a different body answers 409 idempotency_key_reused, and a retry while the first request is still running answers 409 idempotency_key_in_progress. A request that failed does not use up its key, so you can correct it and send it again. After 24 hours a key counts as new, and the same key with a test key and then a live key makes two objects, so never reuse a key for a different write.

Pagination

Lists answer { object: "list", data, hasMore, nextCursor }, newest first. Payments, refunds and events take limit (1 to 100, 25 when left out) and cursor: pass the nextCursor of one page as the cursor of the next, until hasMore is false. Payment links and saved cards come in one list of your 200 newest, with hasMore always false.

The Node.js and PHP libraries read every page for you: listAll, or all, on payments, refunds and events, in both.

Amounts

Every amount is an integer number of cents of LKR, in fields that end in Cents: Rs. 3,500 is 350000. There is no currency field to send and no other currency; answers carry currency: "LKR". A payment is from Rs. 10 (1000) to Rs. 1,000,000 (100000000). A number with a fraction is refused, never rounded.

Errors

An error answers with its HTTP status and a body in one shape: a stable code, a reason when there is a more precise one, a message you can show a person, a docUrl that explains it, and a traceId to quote to support. A refused field is named in fields. Match on code and reason, never on the sentence. Every code and reason, what it means and how to fix it, is on the errors page; each endpoint below lists the ones it can answer with.

Error 400
{
  "code": "VALIDATION_FAILED",
  "message": "Some of the details provided are not valid.",
  "docUrl": "https://payments.lk/developers/errors#validation_failed",
  "traceId": "0f8e3c1a-5b2d-4c7e-9a61-2d4b8f0e7c35",
  "fields": [
    {
      "path": "amountCents",
      "message": "The smallest payment is Rs. 10."
    }
  ]
}

Code and statusWhat it means
VALIDATION_FAILED400The request is not valid
UNAUTHENTICATED401No valid API key
FORBIDDEN403This key cannot do that
NOT_FOUND404No such object for this key
CONFLICT409Not allowed in the current state
RATE_LIMITED429Too many requests
NOT_SUPPORTED501Not available yet
SERVICE_UNAVAILABLE503Temporarily unavailable
INTERNAL500Something went wrong on our side

Retry only what time can change: a 429, a 500, a 503 and idempotency_key_in_progress, each with the same Idempotency-Key. Anything else fails the same way until the request changes.

Rate limits

120 requests a minute from one address, counted separately for each endpoint. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the window resets). Over the limit the answer is 429 RATE_LIMITED with Retry-After in seconds.

Versioning

There is no version header and there are no dated versions: every path starts with /v1. Answers can gain fields, so ignore any field you do not recognise. The OpenAPI document always describes the API as it runs.

Checkouts

A payment and the hosted page that takes it. Most integrations need only this.

Create a checkout

post/v1/checkouts

Creates a payment and the hosted page that takes it. Send amountCents and a description, or lineItems and the total is worked out from them; never both. Send the customer to url. The checkout expires 30 minutes after it is made. The mode comes from the key, never from the body: a sandbox key makes a sandbox checkout on Payable's sandbox. When the customer has paid you receive payment.succeeded; fulfil the order from that webhook, not from the browser's return to successUrl.

Key
A secret key or a publishable key (permission checkouts:create)
Idempotency-Key
Required

With an amount

curl https://api.payments.lk/v1/checkouts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8891" \
  -d '{
    "amountCents": 350000,
    "description": "Two kottu and a milk tea",
    "reference": "order-8891",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "successUrl": "https://yourshop.lk/orders/8891",
    "cancelUrl": "https://yourshop.lk/cart"
  }'

Response 201
{
  "object": "checkout",
  "id": "chk_z6zf7gkx4fcwy3402kmwrm3w",
  "mode": "test",
  "status": "open",
  "url": "https://payments.lk/checkout/chk_z6zf7gkx4fcwy3402kmwrm3w",
  "expiresAt": "2026-09-18T09:44:05.000Z",
  "payment": {
    "object": "payment",
    "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "mode": "test",
    "status": "requires_payment_method",
    "amountCents": 350000,
    "currency": "LKR",
    "description": "Two kottu and a milk tea",
    "reference": "order-8891",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "card": null,
    "cardSave": "not_requested",
    "feeCents": null,
    "netCents": null,
    "refundedCents": 0,
    "failureMessage": null,
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
    "checkoutPageId": null,
    "order": null,
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-18T09:14:05.000Z",
    "succeededAt": null
  }
}

With line items, in Sinhala

curl https://api.payments.lk/v1/checkouts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8892" \
  -d '{
    "lineItems": [
      {
        "name": "Chicken kottu",
        "unitAmountCents": 145000,
        "quantity": 2
      },
      {
        "name": "Milk tea",
        "description": "Hot, with sugar",
        "unitAmountCents": 30000,
        "quantity": 2
      }
    ],
    "reference": "order-8892",
    "locale": "si",
    "successUrl": "https://yourshop.lk/orders/8892",
    "cancelUrl": "https://yourshop.lk/cart"
  }'

Response 201
{
  "object": "checkout",
  "id": "chk_7h2mq8v4d1x9k3r6t0ya5wnp",
  "mode": "test",
  "status": "open",
  "url": "https://payments.lk/si/checkout/chk_7h2mq8v4d1x9k3r6t0ya5wnp",
  "expiresAt": "2026-09-18T09:44:05.000Z",
  "payment": {
    "object": "payment",
    "id": "pay_3c9k2x7m1v8q4r6h0tdy5bnw",
    "mode": "test",
    "status": "requires_payment_method",
    "amountCents": 350000,
    "currency": "LKR",
    "description": "Colombo Kottu House, 4 items",
    "reference": "order-8892",
    "customer": null,
    "card": null,
    "cardSave": "not_requested",
    "feeCents": null,
    "netCents": null,
    "refundedCents": 0,
    "failureMessage": null,
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_7h2mq8v4d1x9k3r6t0ya5wnp",
    "checkoutPageId": null,
    "order": {
      "lines": [
        {
          "id": "line-1",
          "name": "Chicken kottu",
          "quantity": 2,
          "unitAmountCents": 145000,
          "amountCents": 290000,
          "suggested": false
        },
        {
          "id": "line-2",
          "name": "Milk tea",
          "description": "Hot, with sugar",
          "quantity": 2,
          "unitAmountCents": 30000,
          "amountCents": 60000,
          "suggested": false
        }
      ],
      "subtotalCents": 350000,
      "discount": null,
      "shipping": null,
      "tax": null,
      "totalCents": 350000
    },
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-18T09:14:05.000Z",
    "succeededAt": null
  }
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
amountCentsintegerThe amount in LKR cents, from Rs. 10 (1000) to Rs. 1,000,000 (100000000). Required unless you send lineItems, and refused with them. Take it from your own records, never from the browser.1000 to 100000000
descriptionstringWhat the customer is paying for, shown on the checkout. 1 to 120 characters. Required with amountCents; with lineItems a summary such as your trading name and the number of items stands in when you leave it out.1 to 120 characters
lineItemsarray of objectWhat the customer is buying, 1 to 10 lines, shown on the checkout; the total is worked out from them and must come to Rs. 10 to Rs. 1,000,000. Send lineItems or amountCents, never both.
lineItems[].namestringrequiredThe line's name, 1 to 80 characters.1 to 80 characters
lineItems[].descriptionstringMore about the line, up to 200 characters.at most 200 characters
lineItems[].unitAmountCentsintegerrequiredThe price of one, in LKR cents, from Rs. 1 (100) to Rs. 1,000,000 (100000000).100 to 100000000
lineItems[].quantityintegerrequiredHow many, from 1 to 99. The customer cannot change it on the checkout.1 to 99
localeone of: en, si, taThe language the checkout opens in: en, si or ta. English when left out; url then carries /si or /ta.
referencestringYour own reference, such as an order number. Up to 64 characters; it comes back on the payment and its webhooks.at most 64 characters
customerobjectThe customer's details, if you have them. Otherwise the checkout page asks for them.
customer.namestringrequiredThe customer's name, 2 to 80 characters.2 to 80 characters
customer.emailstringrequiredThe customer's email address.3 to 254 characters
customer.phonestringA Sri Lankan phone number, such as 0771234567 or +94771234567. Spaces and dashes are removed.
successUrlstringWhere the customer's browser goes after paying: https, http on localhost, or your app's own scheme such as myshop://paid. It proves nothing; confirm from the webhook.at most 2000 characters
cancelUrlstringWhere the customer's browser goes if they give up. The same rules as successUrl.at most 2000 characters
saveCardbooleanOffer the customer to keep the card on file for later charges. A live checkout refuses it until saved cards are switched on for your account.default false

Returns 201 with a Checkout object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. The line items add up to less or more than one payment may be.json_requiredinvalid_jsonidempotency_key_missingamount
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. A live key before the account is activated, an account that cannot take payments, saveCard on a live checkout before saved cards are switched on, or an amount that would take this month past the plan's monthly limit.idempotency_key_reusedidempotency_key_in_progressmerchant_not_livemerchant_unavailablesaved_cards_offmonthly_limit_reached
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a checkout

get/v1/checkouts/{id}

A checkout and its payment. Read it to confirm an order when your webhook has not arrived; the browser's return proves nothing.

Key
A secret key (permission payments:read)
curl https://api.payments.lk/v1/checkouts/chk_z6zf7gkx4fcwy3402kmwrm3w \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "checkout",
  "id": "chk_z6zf7gkx4fcwy3402kmwrm3w",
  "mode": "test",
  "status": "completed",
  "url": "https://payments.lk/checkout/chk_z6zf7gkx4fcwy3402kmwrm3w",
  "expiresAt": "2026-09-18T09:44:05.000Z",
  "payment": {
    "object": "payment",
    "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "mode": "test",
    "status": "succeeded",
    "amountCents": 350000,
    "currency": "LKR",
    "description": "Two kottu and a milk tea",
    "reference": "order-8891",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "card": {
      "scheme": "VISA",
      "last4": "4242"
    },
    "cardSave": "not_requested",
    "feeCents": 8015,
    "netCents": 341985,
    "refundedCents": 0,
    "failureMessage": null,
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
    "checkoutPageId": null,
    "order": null,
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-18T09:14:05.000Z",
    "succeededAt": "2026-09-18T09:16:41.000Z"
  }
}

Path parameters

NameDescription
idstringrequiredThe checkout's id, chk_ followed by 24 characters.

Returns 200 with a Checkout object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo checkout with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Payments

Every payment, however it was made: a checkout, a payment link, a checkout page or a saved card.

List payments

get/v1/payments

Your payments in this key's mode, newest first. Filter by status and page with limit and cursor.

Key
A secret key (permission payments:read)
curl "https://api.payments.lk/v1/payments?limit=1&status=succeeded" \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "payment",
      "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
      "mode": "test",
      "status": "succeeded",
      "amountCents": 350000,
      "currency": "LKR",
      "description": "Two kottu and a milk tea",
      "reference": "order-8891",
      "customer": {
        "name": "Nimali Perera",
        "email": "[email protected]",
        "phone": "0771234567"
      },
      "card": {
        "scheme": "VISA",
        "last4": "4242"
      },
      "cardSave": "not_requested",
      "feeCents": 8015,
      "netCents": 341985,
      "refundedCents": 0,
      "failureMessage": null,
      "paymentLinkId": null,
      "savedCardId": null,
      "invoiceId": null,
      "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
      "checkoutPageId": null,
      "order": null,
      "delivery": null,
      "customFields": null,
      "customerTaxId": null,
      "termsAcceptedAt": null,
      "createdAt": "2026-09-18T09:14:05.000Z",
      "succeededAt": "2026-09-18T09:16:41.000Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "pay_x1n31wnp8nkbjkkhymtqd6zs"
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
statusone of: requires_payment_method, processing, succeeded, failed, canceled, partially_refunded, refunded, disputedOnly payments in this status.

Returns 200 with a list of Payment objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a payment

get/v1/payments/{id}

One payment.

Key
A secret key (permission payments:read)
curl https://api.payments.lk/v1/payments/pay_x1n31wnp8nkbjkkhymtqd6zs \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "payment",
  "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
  "mode": "test",
  "status": "succeeded",
  "amountCents": 350000,
  "currency": "LKR",
  "description": "Two kottu and a milk tea",
  "reference": "order-8891",
  "customer": {
    "name": "Nimali Perera",
    "email": "[email protected]",
    "phone": "0771234567"
  },
  "card": {
    "scheme": "VISA",
    "last4": "4242"
  },
  "cardSave": "not_requested",
  "feeCents": 8015,
  "netCents": 341985,
  "refundedCents": 0,
  "failureMessage": null,
  "paymentLinkId": null,
  "savedCardId": null,
  "invoiceId": null,
  "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
  "checkoutPageId": null,
  "order": null,
  "delivery": null,
  "customFields": null,
  "customerTaxId": null,
  "termsAcceptedAt": null,
  "createdAt": "2026-09-18T09:14:05.000Z",
  "succeededAt": "2026-09-18T09:16:41.000Z"
}

Path parameters

NameDescription
idstringrequiredThe payment's id, pay_ followed by 24 characters.

Returns 200 with a Payment object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo payment with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Refunds

Money back to the customer's card, in full or in part. The outcome arrives as a webhook.

Create a refund

post/v1/refunds

Refunds a payment in full or in part. The refund is recorded as pending and sent to Payable, which refunds only a settled payment, so a live refund goes out from the next bank working day. The outcome arrives as refund.succeeded or refund.failed. Refunds on one payment never add up to more than was paid.

Key
A secret key (permission refunds:write)
Idempotency-Key
Required
curl https://api.payments.lk/v1/refunds \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refund-order-8891-tea" \
  -d '{
    "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "amountCents": 50000,
    "reason": "The milk tea was not available"
  }'

Response 201
{
  "object": "refund",
  "id": "re_sbca5jeftvf7acqsx5rvdzk3",
  "mode": "test",
  "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
  "status": "pending",
  "amountCents": 50000,
  "currency": "LKR",
  "reason": "The milk tea was not available",
  "failureMessage": null,
  "createdAt": "2026-09-19T10:02:17.000Z",
  "succeededAt": null
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
paymentIdstringrequiredThe payment to refund. It must have succeeded.
amountCentsintegerHow much to refund, in cents, at least Rs. 1 (100). Leave it out to refund everything still refundable.100 to 9007199254740991
reasonstringWhy, for your records. Up to 200 characters.at most 200 characters

Returns 201 with a Refund object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. The amount is more than is left to refund on the payment.json_requiredinvalid_jsonidempotency_key_missingamount
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo payment with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The payment has not succeeded, so there is nothing to refund.idempotency_key_reusedidempotency_key_in_progressnot_refundable
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List refunds

get/v1/refunds

Your refunds in this key's mode, newest first. Filter by payment and page with limit and cursor.

Key
A secret key (permission payments:read)
curl "https://api.payments.lk/v1/refunds?paymentId=pay_x1n31wnp8nkbjkkhymtqd6zs" \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "refund",
      "id": "re_sbca5jeftvf7acqsx5rvdzk3",
      "mode": "test",
      "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
      "status": "succeeded",
      "amountCents": 50000,
      "currency": "LKR",
      "reason": "The milk tea was not available",
      "failureMessage": null,
      "createdAt": "2026-09-19T10:02:17.000Z",
      "succeededAt": "2026-09-19T10:05:52.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
paymentIdstringOnly refunds of this payment.

Returns 200 with a list of Refund objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a refund

get/v1/refunds/{id}

One refund.

Key
A secret key (permission payments:read)
curl https://api.payments.lk/v1/refunds/re_sbca5jeftvf7acqsx5rvdzk3 \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "refund",
  "id": "re_sbca5jeftvf7acqsx5rvdzk3",
  "mode": "test",
  "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
  "status": "succeeded",
  "amountCents": 50000,
  "currency": "LKR",
  "reason": "The milk tea was not available",
  "failureMessage": null,
  "createdAt": "2026-09-19T10:02:17.000Z",
  "succeededAt": "2026-09-19T10:05:52.000Z"
}

Path parameters

NameDescription
idstringrequiredThe refund's id, re_ followed by 24 characters.

Returns 200 with a Refund object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo refund with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Saved cards

Cards customers kept on file at a checkout, charged later without them present.

List saved cards

get/v1/cards

The cards your customers kept on file in this key's mode that have not been deleted, newest first. Filter by the customer's email and page with limit and cursor.

Key
A secret key (permission cards:read)
curl "https://api.payments.lk/v1/cards?limit=1&customerEmail=ruwan%40example.lk" \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "saved_card",
      "id": "card_g2a1rex14ff7qh2dc6j6qzmq",
      "mode": "test",
      "customerEmail": "[email protected]",
      "scheme": "VISA",
      "last4": "4242",
      "expiry": {
        "month": 11,
        "year": 2029
      },
      "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
      "consent": {
        "agreedAt": "2026-09-02T07:30:41.000Z",
        "locale": "en",
        "wording": "checkoutPage.saveCard@1"
      },
      "active": true,
      "createdAt": "2026-09-02T07:30:44.000Z",
      "retiredAt": null
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
customerEmailstringOnly the cards kept by this customer, matched on the address they gave at checkout.3 to 254 characters

Returns 200 with a list of Saved card objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a saved card

get/v1/cards/{id}

One saved card, deleted ones included, so you can still say why a charge stopped. It carries the card's expiry month and the consent the customer gave when they kept it.

Key
A secret key (permission cards:read)
curl https://api.payments.lk/v1/cards/card_g2a1rex14ff7qh2dc6j6qzmq \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "saved_card",
  "id": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "mode": "test",
  "customerEmail": "[email protected]",
  "scheme": "VISA",
  "last4": "4242",
  "expiry": {
    "month": 11,
    "year": 2029
  },
  "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
  "consent": {
    "agreedAt": "2026-09-02T07:30:41.000Z",
    "locale": "en",
    "wording": "checkoutPage.saveCard@1"
  },
  "active": true,
  "createdAt": "2026-09-02T07:30:44.000Z",
  "retiredAt": null
}

Path parameters

NameDescription
idstringrequiredThe saved card's id, card_ followed by 24 characters.

Returns 200 with a Saved card object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo saved card with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Delete a saved card

delete/v1/cards/{id}

Deletes the card here and its token at the processor. Do it when the customer asks or cancels. Deleting a card that is already deleted answers the same card, so a retry is safe.

Key
A secret key (permission cards:charge)
Idempotency-Key
Not used
curl -X DELETE https://api.payments.lk/v1/cards/card_g2a1rex14ff7qh2dc6j6qzmq \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "saved_card",
  "id": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "mode": "test",
  "customerEmail": "[email protected]",
  "scheme": "VISA",
  "last4": "4242",
  "expiry": {
    "month": 11,
    "year": 2029
  },
  "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
  "consent": {
    "agreedAt": "2026-09-02T07:30:41.000Z",
    "locale": "en",
    "wording": "checkoutPage.saveCard@1"
  },
  "active": false,
  "createdAt": "2026-09-02T07:30:44.000Z",
  "retiredAt": "2026-10-04T11:09:52.000Z"
}

Path parameters

NameDescription
idstringrequiredThe saved card's id, card_ followed by 24 characters.

Returns 200 with a Saved card object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo saved card with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Charge a saved card

post/v1/cards/{id}/charge

Takes a payment on a saved card without the customer present. The answer is the payment, usually processing: the outcome arrives as payment.succeeded or payment.failed. A charge the processor refuses at once comes back as failed. Charge only what the customer agreed to, with one Idempotency-Key per billing period.

Key
A secret key (permission cards:charge)
Idempotency-Key
Required
curl https://api.payments.lk/v1/cards/card_g2a1rex14ff7qh2dc6j6qzmq/charge \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sub-1042-2026-10" \
  -d '{
    "amountCents": 250000,
    "description": "October coffee subscription",
    "reference": "sub-1042-2026-10"
  }'

Response 201
{
  "object": "payment",
  "id": "pay_41j10awqfww0v3p1gjndpbv4",
  "mode": "test",
  "status": "processing",
  "amountCents": 250000,
  "currency": "LKR",
  "description": "October coffee subscription",
  "reference": "sub-1042-2026-10",
  "customer": {
    "name": null,
    "email": "[email protected]",
    "phone": null
  },
  "card": {
    "scheme": "VISA",
    "last4": "4242"
  },
  "cardSave": "not_requested",
  "feeCents": null,
  "netCents": null,
  "refundedCents": 0,
  "failureMessage": null,
  "paymentLinkId": null,
  "savedCardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "invoiceId": null,
  "checkoutId": null,
  "checkoutPageId": null,
  "order": null,
  "delivery": null,
  "customFields": null,
  "customerTaxId": null,
  "termsAcceptedAt": null,
  "createdAt": "2026-10-01T06:00:03.000Z",
  "succeededAt": null
}

Path parameters

NameDescription
idstringrequiredThe saved card's id, card_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
amountCentsintegerrequiredThe amount in LKR cents, from Rs. 10 (1000) to Rs. 1,000,000 (100000000). Charge only what the customer agreed to.1000 to 100000000
descriptionstringrequiredWhat the charge is for, 1 to 120 characters.1 to 120 characters
referencestringYour own reference, up to 64 characters.at most 64 characters

Returns 201 with a Payment object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo saved card with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. A live key before the account is activated, a card that cannot be charged again (ask the customer to save it at a new checkout), or an amount that would take this month past the plan's monthly limit.idempotency_key_reusedidempotency_key_in_progressmerchant_not_livecard_not_reusablemonthly_limit_reached
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Events

The events your webhooks receive, readable here too, in the same shape.

List events

get/v1/events

The public events in this key's mode, newest first, in exactly the shape a webhook delivers them. Use it to catch up on anything your endpoint missed. Filter by type and time; page with limit and cursor.

Key
A secret key (permission events:read)
curl "https://api.payments.lk/v1/events?limit=1&type=payment.succeeded" \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "object": "list",
  "data": [
    {
      "id": "evt_ev85ebwj9yqjq81vwf5qwz9m",
      "object": "event",
      "type": "payment.succeeded",
      "mode": "test",
      "created": "2026-09-18T09:16:41.000Z",
      "data": {
        "object": "payment",
        "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
        "mode": "test",
        "status": "succeeded",
        "amountCents": 350000,
        "currency": "LKR",
        "description": "Two kottu and a milk tea",
        "reference": "order-8891",
        "customer": {
          "name": "Nimali Perera",
          "email": "[email protected]",
          "phone": "0771234567"
        },
        "card": {
          "scheme": "VISA",
          "last4": "4242"
        },
        "cardSave": "not_requested",
        "feeCents": 8015,
        "netCents": 341985,
        "refundedCents": 0,
        "failureMessage": null,
        "paymentLinkId": null,
        "savedCardId": null,
        "invoiceId": null,
        "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
        "checkoutPageId": null,
        "order": null,
        "delivery": null,
        "customFields": null,
        "customerTaxId": null,
        "termsAcceptedAt": null,
        "createdAt": "2026-09-18T09:14:05.000Z",
        "succeededAt": "2026-09-18T09:16:41.000Z"
      }
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
typeone of: payment.succeeded, payment.failed, checkout.expired, card.saved, card.save_failed, card.expiring, billing.alert.triggered, subscription_schedule.created, subscription_schedule.completed, subscription_schedule.released, subscription_schedule.canceled, refund.succeeded, refund.failed, account.limit.approaching, account.limit.reached, account.limit.reset, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.trial_will_end, customer.subscription.paused, customer.subscription.resumed, invoice.created, invoice.finalized, invoice.sent, invoice.overdue, credit_note.created, entitlements.active_entitlement_summary.updated, invoice.paid, invoice.payment_failed, invoice.upcoming, invoice.voided, invoice.marked_uncollectibleOnly events of this type.
sincestring, ISO 8601 timeOnly events created at or after this moment, ISO 8601, such as 2026-09-18T00:00:00Z.

Returns 200 with a list of Event objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve an event

get/v1/events/{id}

One event, in the shape a webhook delivers it.

Key
A secret key (permission events:read)
curl https://api.payments.lk/v1/events/evt_ev85ebwj9yqjq81vwf5qwz9m \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "id": "evt_ev85ebwj9yqjq81vwf5qwz9m",
  "object": "event",
  "type": "payment.succeeded",
  "mode": "test",
  "created": "2026-09-18T09:16:41.000Z",
  "data": {
    "object": "payment",
    "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "mode": "test",
    "status": "succeeded",
    "amountCents": 350000,
    "currency": "LKR",
    "description": "Two kottu and a milk tea",
    "reference": "order-8891",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "card": {
      "scheme": "VISA",
      "last4": "4242"
    },
    "cardSave": "not_requested",
    "feeCents": 8015,
    "netCents": 341985,
    "refundedCents": 0,
    "failureMessage": null,
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
    "checkoutPageId": null,
    "order": null,
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-18T09:14:05.000Z",
    "succeededAt": "2026-09-18T09:16:41.000Z"
  }
}

Path parameters

NameDescription
idstringrequiredThe event's id, evt_ followed by 24 characters.

Returns 200 with an Event object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo public event with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Subscriptions

Customers, recurring prices, subscriptions and their invoices. We renew, charge the card on file, retry and email the customer; you react to the events.

Create a customer

post/v1/customers

Someone you bill. One customer per email in each mode: sending an email you already have answers that customer, with any new details filled in.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a customer: request
curl https://api.payments.lk/v1/customers \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: member-1042" \
  -d '{
    "email": "[email protected]",
    "name": "Nimali Perera",
    "phone": "0771234567",
    "locale": "si"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "customer",
  "id": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "mode": "test",
  "email": "[email protected]",
  "name": "Nimali Perera",
  "phone": "+94771234567",
  "locale": "si",
  "defaultCardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "creditBalanceCents": 0,
  "createdAt": "2026-10-01T04:12:30.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
emailstringrequiredTheir email address. A customer with this email in this mode is answered, not made again.3 to 254 characters
namestringTheir name, 1 to 120 characters.1 to 120 characters
phonestringA Sri Lankan mobile number, such as 0771234567.at most 24 characters
localeone of: en, si, taThe language of their emails and payment pages: en (the default), si or ta.

Returns 201 with a Customer object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List customers

get/v1/customers

Your customers in this key's mode, newest first. Filter by email; page with limit and cursor.

Key
A secret key (permission billing:read)
List customers: request
curl "https://api.payments.lk/v1/customers?email=nimali%40example.com" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "customer",
      "id": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "mode": "test",
      "email": "[email protected]",
      "name": "Nimali Perera",
      "phone": "+94771234567",
      "locale": "si",
      "defaultCardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
      "creditBalanceCents": 0,
      "createdAt": "2026-10-01T04:12:30.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
emailstringOnly the customer with this email.3 to 254 characters

Returns 200 with a list of Customer objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a customer

get/v1/customers/{id}

One customer.

Key
A secret key (permission billing:read)
Retrieve a customer: request
curl https://api.payments.lk/v1/customers/cus_q8w3n5v1x7c2m9r4t6ya0kzp \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "customer",
  "id": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "mode": "test",
  "email": "[email protected]",
  "name": "Nimali Perera",
  "phone": "+94771234567",
  "locale": "si",
  "defaultCardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "creditBalanceCents": 0,
  "createdAt": "2026-10-01T04:12:30.000Z"
}

Path parameters

NameDescription
idstringrequiredThe customer's id, cus_ followed by 24 characters.

Returns 200 with a Customer object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a price

post/v1/prices

A recurring price: an amount every day, week, month or year, on a product you name here or made before. Prices are never edited; make a new one and archive the old in the dashboard.

Key
A secret key (permission billing:write)
Idempotency-Key
Required

A monthly price

Create a price, A monthly price: request
curl https://api.payments.lk/v1/prices \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: price-gym-monthly" \
  -d '{
    "product": {
      "name": "Gym membership",
      "description": "Unlimited visits, both branches"
    },
    "unitAmountCents": 450000,
    "interval": "month"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "price",
  "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
  "mode": "test",
  "product": {
    "id": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
    "name": "Gym membership",
    "description": "Unlimited visits, both branches"
  },
  "unitAmountCents": 450000,
  "currency": "LKR",
  "interval": "month",
  "intervalCount": 1,
  "nickname": null,
  "active": true,
  "billingScheme": "per_unit",
  "tiersMode": null,
  "tiers": null,
  "packageSize": null,
  "usageType": "licensed",
  "meterId": null,
  "createdAt": "2026-10-01T04:00:00.000Z"
}

Usage, tiered: messages sent

Create a price, Usage, tiered: messages sent: request
curl https://api.payments.lk/v1/prices \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: price-sms-usage" \
  -d '{
    "product": {
      "name": "SMS gateway"
    },
    "interval": "month",
    "billingScheme": "tiered",
    "tiersMode": "graduated",
    "tiers": [
      {
        "upTo": 1000,
        "unitAmountCents": 150
      },
      {
        "upTo": null,
        "unitAmountCents": 100
      }
    ],
    "usageType": "metered",
    "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "price",
  "id": "price_u6s2v9x1c8q4m7k3r0tnhw5y",
  "mode": "test",
  "product": {
    "id": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
    "name": "SMS gateway",
    "description": null
  },
  "unitAmountCents": 0,
  "currency": "LKR",
  "interval": "month",
  "intervalCount": 1,
  "nickname": null,
  "active": true,
  "billingScheme": "tiered",
  "tiersMode": "graduated",
  "tiers": [
    {
      "upTo": 1000,
      "unitAmountCents": 150,
      "flatAmountCents": 0
    },
    {
      "upTo": null,
      "unitAmountCents": 100,
      "flatAmountCents": 0
    }
  ],
  "packageSize": null,
  "usageType": "metered",
  "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "createdAt": "2026-10-01T04:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
productIdstringA product of yours already made. Send this or product, not both.
productobjectA product made with this price. Send this or productId, not both.
product.namestringrequiredThe product's name, 1 to 120 characters.1 to 120 characters
product.descriptionstringA line about it, up to 500 characters.at most 500 characters
unitAmountCentsintegerThe amount per unit (or per package), in LKR cents, up to Rs. 1,000,000 (100000000). A licensed price per unit is Rs. 10 (1000) at least; a metered or package price may be less. Leave it out for a tiered price.0 to 100000000
intervalone of: day, week, month, yearrequiredHow often it bills: day, week, month or year.
intervalCountintegerHow many intervals a period is, 1 to 12. Defaults to 1; a yearly price is every year.1 to 12; default 1
nicknamestringYour own name for it on invoices, up to 80 characters.at most 80 characters
billingSchemeone of: per_unit, package, tieredper_unit (the default), package or tiered.default "per_unit"
tiersModeone of: graduated, volumeWith tiered: graduated or volume.
tiersarray of objectWith tiered: 2 to 10 tiers in order, each upTo above the one before, the last upTo null.
tiers[].upTointeger or nullrequiredThe last unit the tier prices, or null for the last tier.at least 1
tiers[].unitAmountCentsintegerrequiredPer unit in the tier, in cents, from 0.0 to 100000000
tiers[].flatAmountCentsintegerAdded once for the tier, in cents. Defaults to 0.0 to 100000000; default 0
packageSizeintegerWith package: how many units a package is, 2 to 1000000.2 to 1000000
usageTypeone of: licensed, meteredlicensed (the default: a quantity, billed in advance) or metered (a meter's usage, billed in arrears).default "licensed"
meterIdstringWith metered: the active meter whose usage it bills.

Returns 201 with a Price object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. A metered price names no active meter of yours, or the tiers cannot price anything.json_requiredinvalid_jsonidempotency_key_missingmeter_invalidprice_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo product with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List prices

get/v1/prices

Your prices in this key's mode, newest first, with their products. Filter by active.

Key
A secret key (permission billing:read)
List prices: request
curl "https://api.payments.lk/v1/prices?active=true" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "price",
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "mode": "test",
      "product": {
        "id": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
        "name": "Gym membership",
        "description": "Unlimited visits, both branches"
      },
      "unitAmountCents": 450000,
      "currency": "LKR",
      "interval": "month",
      "intervalCount": 1,
      "nickname": null,
      "active": true,
      "billingScheme": "per_unit",
      "tiersMode": null,
      "tiers": null,
      "packageSize": null,
      "usageType": "licensed",
      "meterId": null,
      "createdAt": "2026-10-01T04:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
activeone of: true, falsetrue for prices taking new subscriptions, false for archived ones.

Returns 200 with a list of Price objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a price

get/v1/prices/{id}

One price, with its product.

Key
A secret key (permission billing:read)
Retrieve a price: request
curl https://api.payments.lk/v1/prices/price_m3v8q1x6c9k2r5t7y0wn4hzp \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "price",
  "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
  "mode": "test",
  "product": {
    "id": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
    "name": "Gym membership",
    "description": "Unlimited visits, both branches"
  },
  "unitAmountCents": 450000,
  "currency": "LKR",
  "interval": "month",
  "intervalCount": 1,
  "nickname": null,
  "active": true,
  "billingScheme": "per_unit",
  "tiersMode": null,
  "tiers": null,
  "packageSize": null,
  "usageType": "licensed",
  "meterId": null,
  "createdAt": "2026-10-01T04:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe price's id, price_ followed by 24 characters.

Returns 200 with a Price object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo price with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a subscription

post/v1/subscriptions

Starts a subscription on a price for a customer, by id or by details. It is incomplete, with its first invoice open, until that invoice is paid. With a card the customer saved with you (cardId, or the customer's default card) the invoice is charged within a minute. Without one, send the customer to checkoutUrl: they pay on Payable's page and agree to keep the card for the renewals. Unpaid after 23 hours, the subscription expires. From then on we renew each period, charge the card, retry a decline on days 1, 3, 7 and 14, email the customer, and send you the events. Live subscriptions need saved cards switched on.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a subscription: request
curl https://api.payments.lk/v1/subscriptions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: member-1042-gym" \
  -d '{
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "priceId": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
    "reference": "member-1042",
    "successUrl": "https://gym.example/welcome"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "subscription",
  "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "status": "incomplete",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "price": {
    "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
    "productName": "Gym membership",
    "unitAmountCents": 450000,
    "interval": "month",
    "intervalCount": 1
  },
  "quantity": 1,
  "items": [
    {
      "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
      "price": {
        "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
        "productName": "Gym membership",
        "unitAmountCents": 450000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1
    }
  ],
  "currentPeriodStart": "2026-10-01T04:12:31.000Z",
  "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
  "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": null,
  "endedAt": null,
  "cancelReason": null,
  "cancellationDetails": {
    "reason": null,
    "feedback": null,
    "comment": null
  },
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "create_invoice",
  "pauseCollection": null,
  "discount": null,
  "taxRateIds": [],
  "subscriptionLinkId": null,
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "pendingUpdate": null,
  "cardId": null,
  "cardMissingSince": null,
  "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "checkoutUrl": "https://payments.lk/checkout/chk_r8v2x6c1q9m4k7t3y0wnzh5p",
  "reference": "member-1042",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
customerIdstringYour customer, by id. Send this or customer, not both.
customerCreateCustomerRequestYour customer's details: found by email, or made. Send this or customerId, not both.
priceIdstringThe active price to bill. Send this or items.
quantityintegerHow many units each period, 1 to 10000. Defaults to 1.1 to 10000; default 1
itemsarray of objectUp to 10 active prices, each once and all on the same interval, such as a base plan and add-ons. Send this or priceId.
items[].priceIdstringrequiredThe price.
items[].quantityintegerHow many units of it each period, 1 to 10000. Defaults to 1.1 to 10000; default 1
cardIdstringA card this customer kept with you: the first invoice is charged to it. Without it, the customer's default card, and without that, checkoutUrl.
referencestringYour own reference, up to 120 characters.at most 120 characters
successUrlstringWhere the customer lands after the first payment at checkoutUrl: https, or your app's scheme.at most 2000 characters
cancelUrlstringWhere the customer lands if they leave checkoutUrl without paying.at most 2000 characters
trialDaysintegerA free trial of this many days, 1 to 730: the subscription starts trialing, with no payment, and its first invoice is for nothing. Send this or trialEnd.1 to 730
trialEndstring, ISO 8601 timeA free trial until this time, ISO 8601, within two years. Send this or trialDays.
trialEndBehaviorone of: create_invoice, pause, cancelWith no card on file when the trial ends: create_invoice (the default: the customer is sent the invoice to pay), pause, or cancel.default "create_invoice"
cancelAtstring, ISO 8601 timeEnds the subscription at this time, ISO 8601, within two years; the last period is billed only up to it.
couponIdstringA coupon of yours discounting its invoices.
taxRateIdsarray of stringUp to 5 tax rates of yours applied to its invoices.
addInvoiceItemsarray of objectUp to 10 one-off lines on its first invoice, such as a joining fee.
addInvoiceItems[].descriptionstringrequiredThe line as the customer reads it.1 to 200 characters
addInvoiceItems[].unitAmountCentsintegerrequiredPer unit, in cents; negative for a credit.-100000000 to 100000000
addInvoiceItems[].quantityintegerHow many units, 1 to 10000. Defaults to 1.1 to 10000; default 1
addInvoiceItems[].discountablebooleanWhether a coupon discounts it. Defaults to true.default true
collectionMethodone of: charge_automatically, send_invoicecharge_automatically (the default) or send_invoice: it starts active, and each invoice is emailed to the customer to pay by its due date instead of being charged.default "charge_automatically"
daysUntilDueintegerFor send_invoice: days from an invoice being sent to its due date, 0 to 365. Defaults to 30.0 to 365
billingCycleAnchorone of: now, first_of_month or string, ISO 8601 timeWhere its renewals fall: now (from the moment it starts), first_of_month (midnight on the 1st of each month in Sri Lanka; monthly and yearly prices only), or an ISO 8601 time after now and at most one period ahead. The part period up to it is its first, invoiced now and priced by the day (or the second, as your billing settings say); one worth less than Rs. 10 is added to the first renewal's invoice instead. Not with a trial, whose end anchors the renewals. Leave it out for your billing settings' default.

Returns 201 with a Subscription object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. trialEnd or cancelAt is in the past or more than two years away.json_requiredinvalid_jsonidempotency_key_missingbilling_date_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. An archived price, a card that is not this customer's, a coupon or tax rate that cannot be used, live saved cards not switched on, the monthly limit, or an account that cannot take payments.idempotency_key_reusedidempotency_key_in_progressprice_unavailablecard_not_reusablecoupon_invalidtax_rate_invalidsaved_cards_offmonthly_limit_reachedmerchant_not_livemerchant_unavailable
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List subscriptions

get/v1/subscriptions

Your subscriptions in this key's mode, newest first. Filter by customer and status.

Key
A secret key (permission billing:read)
List subscriptions: request
curl "https://api.payments.lk/v1/subscriptions?status=active" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "subscription",
      "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
      "mode": "test",
      "status": "active",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "price": {
        "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
        "productName": "Gym membership",
        "unitAmountCents": 450000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1,
      "items": [
        {
          "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
          "price": {
            "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
            "productName": "Gym membership",
            "unitAmountCents": 450000,
            "interval": "month",
            "intervalCount": 1
          },
          "quantity": 1
        }
      ],
      "currentPeriodStart": "2026-10-01T04:12:31.000Z",
      "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
      "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
      "cancelAtPeriodEnd": false,
      "cancelAt": null,
      "canceledAt": null,
      "endedAt": null,
      "cancelReason": null,
      "cancellationDetails": {
        "reason": null,
        "feedback": null,
        "comment": null
      },
      "trialStart": null,
      "trialEnd": null,
      "trialEndBehavior": "create_invoice",
      "pauseCollection": null,
      "discount": null,
      "taxRateIds": [],
      "subscriptionLinkId": null,
      "collectionMethod": "charge_automatically",
      "daysUntilDue": null,
      "pendingUpdate": null,
      "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
      "cardMissingSince": null,
      "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
      "checkoutUrl": null,
      "reference": "member-1042",
      "createdAt": "2026-10-01T04:12:31.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
customerIdstringOnly this customer's subscriptions.
statusone of: incomplete, incomplete_expired, trialing, active, past_due, unpaid, paused, canceledOnly subscriptions in this status.

Returns 200 with a list of Subscription objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a subscription

get/v1/subscriptions/{id}

One subscription.

Key
A secret key (permission billing:read)
Retrieve a subscription: request
curl https://api.payments.lk/v1/subscriptions/sub_p6x1c8v3q9m2k7r4t0yhwz5n \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription",
  "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "status": "active",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "price": {
    "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
    "productName": "Gym membership",
    "unitAmountCents": 450000,
    "interval": "month",
    "intervalCount": 1
  },
  "quantity": 1,
  "items": [
    {
      "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
      "price": {
        "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
        "productName": "Gym membership",
        "unitAmountCents": 450000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1
    }
  ],
  "currentPeriodStart": "2026-10-01T04:12:31.000Z",
  "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
  "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": null,
  "endedAt": null,
  "cancelReason": null,
  "cancellationDetails": {
    "reason": null,
    "feedback": null,
    "comment": null
  },
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "create_invoice",
  "pauseCollection": null,
  "discount": null,
  "taxRateIds": [],
  "subscriptionLinkId": null,
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "pendingUpdate": null,
  "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "cardMissingSince": null,
  "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "checkoutUrl": null,
  "reference": "member-1042",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription's id, sub_ followed by 24 characters.

Returns 200 with a Subscription object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Update a subscription

post/v1/subscriptions/{id}

Changes what a subscription bills (items, or priceId and quantity for one item) and how the part of the period already begun is billed (prorationBehavior), when it ends (cancelAtPeriodEnd, cancelAt), pauses or resumes collection (pauseCollection), ends or moves a trial (trialEnd), and sets the card renewals are charged to, the cancellation details, the coupon, the tax rates and your reference. Only the fields you send change. A change of interval starts a new period at once, invoiced now with the unused time credited. With paymentBehavior pending_if_incomplete a change that costs more waits in pendingUpdate until its invoice is paid. Preview a change with POST /v1/invoices/preview. customer.subscription.updated is sent.

Key
A secret key (permission billing:write)
Idempotency-Key
Required

Move to another plan, invoiced now

Update a subscription, Move to another plan, invoiced now: request
curl https://api.payments.lk/v1/subscriptions/sub_p6x1c8v3q9m2k7r4t0yhwz5n \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upgrade-member-1042-pool" \
  -d '{
    "priceId": "price_w7k2x9c4q1m8v3r6t0ynhz5p",
    "prorationBehavior": "always_invoice",
    "prorationDate": "2026-10-16T04:12:31.000Z"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription",
  "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "status": "active",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "price": {
    "id": "price_w7k2x9c4q1m8v3r6t0ynhz5p",
    "productName": "Gym and pool",
    "unitAmountCents": 600000,
    "interval": "month",
    "intervalCount": 1
  },
  "quantity": 1,
  "items": [
    {
      "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
      "price": {
        "id": "price_w7k2x9c4q1m8v3r6t0ynhz5p",
        "productName": "Gym and pool",
        "unitAmountCents": 600000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1
    }
  ],
  "currentPeriodStart": "2026-10-01T04:12:31.000Z",
  "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
  "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": null,
  "endedAt": null,
  "cancelReason": null,
  "cancellationDetails": {
    "reason": null,
    "feedback": null,
    "comment": null
  },
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "create_invoice",
  "pauseCollection": null,
  "discount": null,
  "taxRateIds": [],
  "subscriptionLinkId": null,
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "pendingUpdate": null,
  "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "cardMissingSince": null,
  "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "checkoutUrl": null,
  "reference": "member-1042",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Pause collection over the holidays

Update a subscription, Pause collection over the holidays: request
curl https://api.payments.lk/v1/subscriptions/sub_p6x1c8v3q9m2k7r4t0yhwz5n \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pause-member-1042-holidays" \
  -d '{
    "pauseCollection": {
      "behavior": "keep_as_draft",
      "resumesAt": "2027-01-05T00:00:00.000Z"
    }
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription",
  "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "status": "active",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "price": {
    "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
    "productName": "Gym membership",
    "unitAmountCents": 450000,
    "interval": "month",
    "intervalCount": 1
  },
  "quantity": 1,
  "items": [
    {
      "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
      "price": {
        "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
        "productName": "Gym membership",
        "unitAmountCents": 450000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1
    }
  ],
  "currentPeriodStart": "2026-10-01T04:12:31.000Z",
  "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
  "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": null,
  "endedAt": null,
  "cancelReason": null,
  "cancellationDetails": {
    "reason": null,
    "feedback": null,
    "comment": null
  },
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "create_invoice",
  "pauseCollection": {
    "behavior": "keep_as_draft",
    "resumesAt": "2027-01-05T00:00:00.000Z"
  },
  "discount": null,
  "taxRateIds": [],
  "subscriptionLinkId": null,
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "pendingUpdate": null,
  "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "cardMissingSince": null,
  "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "checkoutUrl": null,
  "reference": "member-1042",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription's id, sub_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
itemsarray of objectChanges to its items, up to 10: an existing one by id (a new priceId, a new quantity, or deleted true), or a new one by priceId.
items[].idstringThe item to change, from the subscription's items.
items[].priceIdstringIts new price, or the price of a new item. Active, and on the subscription's interval unless every item changes to a new one together.
items[].quantityintegerIts new quantity, 1 to 10000.1 to 10000
items[].deleted"true"True removes the item. A subscription keeps at least one.
priceIdstringFor a subscription of one item: its new price. Send this and quantity, or items.
quantityintegerFor a subscription of one item: its new quantity, 1 to 10000.1 to 10000
prorationBehaviorone of: create_prorations, always_invoice, noneHow the part of the period already begun is billed: create_prorations (the default: a credit for the unused time and a charge for the rest of the period wait for the next invoice), always_invoice (the same, invoiced and charged now) or none. A change of interval starts a new period at once, invoiced now.
prorationDatestring, ISO 8601 timePrice the change as at this time, ISO 8601: inside the current period and not after now. Send the time a preview used so the charge matches it.
paymentBehaviorone of: allow_incomplete, pending_if_incompleteallow_incomplete (the default: the change applies at once) or pending_if_incomplete (a change that costs more waits for its invoice to be paid; see pendingUpdate).
cancelAtPeriodEndbooleanTrue ends it when the period that is paid for runs out; false takes that back.
cancelAtstring, ISO 8601 time or nullEnds it at this time, ISO 8601, within two years; the last period is billed only up to it. Null takes the date back.
pauseCollectionobject or nullPauses collection: invoices are still made but not charged, until resumesAt or until you send null.
pauseCollection.behaviorone of: keep_as_draft, mark_uncollectible, voidrequiredkeep_as_draft, mark_uncollectible or void: what happens to each invoice made while paused.
pauseCollection.resumesAtstring, ISO 8601 timeWhen collection resumes by itself, ISO 8601, within two years.
trialEnd"now" or string, ISO 8601 timeEnds a trialing subscription's trial: "now", or a new time within two years.
trialEndBehaviorone of: create_invoice, pause, cancelcreate_invoice, pause or cancel: what happens if the trial ends with no card on file.
cardIdstringA card this customer kept with you, charged for the renewals from now on.
cancellationDetailsobjectWhy it is being cancelled, kept on the subscription.
cancellationDetails.feedbackone of: customer_service, low_quality, missing_features, other, switched_service, too_complex, too_expensive, unusedcustomer_service, low_quality, missing_features, other, switched_service, too_complex, too_expensive or unused.
cancellationDetails.commentstringA comment on why, up to 500 characters.at most 500 characters
referencestring or nullYour own reference, up to 120 characters, or null to clear it.at most 120 characters
couponIdstring or nullA coupon to discount its invoices from now, or null to take the discount off.
taxRateIdsarray of stringThe tax rates for its invoices from now; an empty list stops taxing them.
collectionMethodone of: charge_automatically, send_invoiceHow its invoices are collected from the next one: charge_automatically or send_invoice.
daysUntilDueintegerFor send_invoice: days from an invoice being sent to its due date, 0 to 365.0 to 365

Returns 200 with a Subscription object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. A date is out of range, or the items would not go together: an unknown item, a price twice, two intervals, or none left.json_requiredinvalid_jsonidempotency_key_missingbilling_date_invaliditems_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The subscription has ended, the change does not apply in its status (or a change already waits for its invoice), the card is not this customer's, a price is archived or in the other mode, or a coupon or tax rate cannot be used.idempotency_key_reusedidempotency_key_in_progresssubscription_endedsubscription_change_refusedcard_not_reusableprice_unavailablecoupon_invalidtax_rate_invalid
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Cancel a subscription

post/v1/subscriptions/{id}/cancel

Ends a subscription now (atPeriodEnd false, the default): its open invoices are voided and retries stop. Or at the end of the period that is paid for (atPeriodEnd true), which you can take back with resume until then. The customer is emailed when it ends.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Cancel a subscription: request
curl https://api.payments.lk/v1/subscriptions/sub_p6x1c8v3q9m2k7r4t0yhwz5n/cancel \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cancel-member-1042" \
  -d '{
    "atPeriodEnd": false
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription",
  "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "status": "canceled",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "price": {
    "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
    "productName": "Gym membership",
    "unitAmountCents": 450000,
    "interval": "month",
    "intervalCount": 1
  },
  "quantity": 1,
  "items": [
    {
      "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
      "price": {
        "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
        "productName": "Gym membership",
        "unitAmountCents": 450000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1
    }
  ],
  "currentPeriodStart": "2026-10-01T04:12:31.000Z",
  "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
  "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": "2026-10-20T08:00:00.000Z",
  "endedAt": "2026-10-20T08:00:00.000Z",
  "cancelReason": "requested_by_merchant",
  "cancellationDetails": {
    "reason": "requested_by_merchant",
    "feedback": "too_expensive",
    "comment": "Moving to the branch closer to home."
  },
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "create_invoice",
  "pauseCollection": null,
  "discount": null,
  "taxRateIds": [],
  "subscriptionLinkId": null,
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "pendingUpdate": null,
  "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "cardMissingSince": null,
  "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "checkoutUrl": null,
  "reference": "member-1042",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription's id, sub_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
atPeriodEndbooleanFalse (the default) ends it now. True lets the period that is paid for run out first.default false
feedbackone of: customer_service, low_quality, missing_features, other, switched_service, too_complex, too_expensive, unusedWhy, in Stripe's words: customer_service, low_quality, missing_features, other, switched_service, too_complex, too_expensive or unused.
commentstringA comment on why, up to 500 characters.at most 500 characters

Returns 200 with a Subscription object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The subscription has already ended.idempotency_key_reusedidempotency_key_in_progresssubscription_ended
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Resume a subscription

post/v1/subscriptions/{id}/resume

Withdraws a cancellation set for the period's end or for a date, while it is still running. A paused subscription (its trial ended with no card) starts again with a new period from now, billed at once to the card now on file, and customer.subscription.resumed is sent. A subscription with neither is answered as it is.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Resume a subscription: request
curl -X POST https://api.payments.lk/v1/subscriptions/sub_p6x1c8v3q9m2k7r4t0yhwz5n/resume \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: resume-member-1042"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription",
  "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "status": "active",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "price": {
    "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
    "productName": "Gym membership",
    "unitAmountCents": 450000,
    "interval": "month",
    "intervalCount": 1
  },
  "quantity": 1,
  "items": [
    {
      "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
      "price": {
        "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
        "productName": "Gym membership",
        "unitAmountCents": 450000,
        "interval": "month",
        "intervalCount": 1
      },
      "quantity": 1
    }
  ],
  "currentPeriodStart": "2026-10-01T04:12:31.000Z",
  "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
  "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": null,
  "endedAt": null,
  "cancelReason": null,
  "cancellationDetails": {
    "reason": null,
    "feedback": null,
    "comment": null
  },
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "create_invoice",
  "pauseCollection": null,
  "discount": null,
  "taxRateIds": [],
  "subscriptionLinkId": null,
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "pendingUpdate": null,
  "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
  "cardMissingSince": null,
  "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "checkoutUrl": null,
  "reference": "member-1042",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription's id, sub_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with a Subscription object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The subscription has already ended, or it is paused and there is no card on file.idempotency_key_reusedidempotency_key_in_progresssubscription_endedsubscription_change_refused
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List invoices

get/v1/invoices

Your invoices in this key's mode, newest first. Filter by subscription, customer and status.

Key
A secret key (permission billing:read)
List invoices: request
curl "https://api.payments.lk/v1/invoices?subscriptionId=sub_p6x1c8v3q9m2k7r4t0yhwz5n" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "invoice",
      "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
      "mode": "test",
      "number": "INV-000002",
      "status": "open",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
      "billingReason": "subscription_cycle",
      "periodStart": "2026-11-01T04:12:31.000Z",
      "periodEnd": "2026-12-01T04:12:31.000Z",
      "lines": [
        {
          "description": "Gym membership, every month",
          "quantity": 1,
          "unitAmountCents": 450000,
          "amountCents": 450000,
          "periodStart": "2026-11-01T04:12:31.000Z",
          "periodEnd": "2026-12-01T04:12:31.000Z",
          "proration": false
        }
      ],
      "subtotalCents": 450000,
      "discountCents": 0,
      "couponId": null,
      "taxCents": 0,
      "taxes": [],
      "totalCents": 450000,
      "creditAppliedCents": 0,
      "amountDueCents": 450000,
      "amountPaidCents": 0,
      "creditNotesCents": 0,
      "collectionMethod": "charge_automatically",
      "dueDate": null,
      "memo": null,
      "currency": "LKR",
      "attemptCount": 1,
      "nextAttemptAt": "2026-11-02T03:30:00.000Z",
      "lastFailure": "The processor refused the charge.",
      "paidAt": null,
      "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
      "createdAt": "2026-11-01T04:13:00.000Z"
    },
    {
      "object": "invoice",
      "id": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
      "mode": "test",
      "number": "INV-000001",
      "status": "paid",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
      "billingReason": "subscription_create",
      "periodStart": "2026-10-01T04:12:31.000Z",
      "periodEnd": "2026-11-01T04:12:31.000Z",
      "lines": [
        {
          "description": "Gym membership, every month",
          "quantity": 1,
          "unitAmountCents": 450000,
          "amountCents": 450000,
          "periodStart": "2026-10-01T04:12:31.000Z",
          "periodEnd": "2026-11-01T04:12:31.000Z",
          "proration": false
        }
      ],
      "subtotalCents": 450000,
      "discountCents": 0,
      "couponId": null,
      "taxCents": 0,
      "taxes": [],
      "totalCents": 450000,
      "creditAppliedCents": 0,
      "amountDueCents": 450000,
      "amountPaidCents": 450000,
      "creditNotesCents": 0,
      "collectionMethod": "charge_automatically",
      "dueDate": null,
      "memo": null,
      "currency": "LKR",
      "attemptCount": 0,
      "nextAttemptAt": null,
      "lastFailure": null,
      "paidAt": "2026-10-01T04:15:02.000Z",
      "hostedInvoiceUrl": "https://payments.lk/invoice/q7Vd2x9Kc4mZt1Wn8Rb5Hy3Lp6Gs0Jf2Ae4Uo7Ik9Nx",
      "createdAt": "2026-10-01T04:12:31.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
subscriptionIdstringOnly this subscription's invoices.
customerIdstringOnly this customer's invoices.
statusone of: draft, open, paid, void, uncollectibleOnly invoices in this status.

Returns 200 with a list of Invoice objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create an invoice

post/v1/invoices

A standalone invoice for a customer, not tied to a subscription: a draft whose lines are the customer's pending invoice items (make them with POST /v1/invoice_items first, or add them to the draft with its invoiceId). Finalise it with POST /v1/invoices/{id}/finalize: send_invoice (the default) numbers it and emails the customer its page, to pay by the due date; charge_automatically charges the card on file.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create an invoice: request
curl https://api.payments.lk/v1/invoices \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: term-3-nimali" \
  -d '{
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "collectionMethod": "send_invoice",
    "daysUntilDue": 30,
    "memo": "Third term, grade 10"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "invoice",
  "id": "in_s5v1x8c3q9m2k7r4t0yhwn6z",
  "mode": "test",
  "number": null,
  "status": "draft",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": null,
  "billingReason": "manual",
  "periodStart": null,
  "periodEnd": null,
  "lines": [
    {
      "description": "Grade 10 maths, third term",
      "quantity": 1,
      "unitAmountCents": 1800000,
      "amountCents": 1800000,
      "periodStart": null,
      "periodEnd": null,
      "proration": false
    }
  ],
  "subtotalCents": 1800000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 1800000,
  "creditAppliedCents": 0,
  "amountDueCents": 1800000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "send_invoice",
  "dueDate": null,
  "memo": "Third term, grade 10",
  "currency": "LKR",
  "attemptCount": 0,
  "nextAttemptAt": null,
  "lastFailure": null,
  "paidAt": null,
  "hostedInvoiceUrl": null,
  "createdAt": "2026-10-01T08:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
customerIdstringrequiredThe customer, one of yours in this key's mode. Their pending invoice items not for a subscription become its lines.
collectionMethodone of: charge_automatically, send_invoicesend_invoice (the default: emailed when finalised, to pay by its due date) or charge_automatically (the card on file is charged when finalised).default "send_invoice"
daysUntilDueintegerFor send_invoice: days from sending to the due date, 0 to 365. Defaults to 30.0 to 365
memostringA note printed on it, up to 500 characters.at most 500 characters

Returns 201 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve an invoice

get/v1/invoices/{id}

One invoice, with its lines and where its collection stands.

Key
A secret key (permission billing:read)
Retrieve an invoice: request
curl https://api.payments.lk/v1/invoices/in_c4v9x2q7m1k8r3t6y0wnzhp5 \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "mode": "test",
  "number": "INV-000001",
  "status": "paid",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_create",
  "periodStart": "2026-10-01T04:12:31.000Z",
  "periodEnd": "2026-11-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Gym membership, every month",
      "quantity": 1,
      "unitAmountCents": 450000,
      "amountCents": 450000,
      "periodStart": "2026-10-01T04:12:31.000Z",
      "periodEnd": "2026-11-01T04:12:31.000Z",
      "proration": false
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 450000,
  "creditAppliedCents": 0,
  "amountDueCents": 450000,
  "amountPaidCents": 450000,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 0,
  "nextAttemptAt": null,
  "lastFailure": null,
  "paidAt": "2026-10-01T04:15:02.000Z",
  "hostedInvoiceUrl": "https://payments.lk/invoice/q7Vd2x9Kc4mZt1Wn8Rb5Hy3Lp6Gs0Jf2Ae4Uo7Ik9Nx",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe invoice's id, in_ followed by 24 characters.

Returns 200 with an Invoice object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Preview the next invoice

get/v1/invoices/upcoming

The invoice a subscription's next renewal will make, as it stands now: its period and its lines. Nothing is written, and the preview has no id of its own. Not found for a subscription that will not renew.

Key
A secret key (permission billing:read)
Preview the next invoice: request
curl "https://api.payments.lk/v1/invoices/upcoming?subscriptionId=sub_p6x1c8v3q9m2k7r4t0yhwz5n" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "upcoming_sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "number": null,
  "status": "draft",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_cycle",
  "periodStart": "2026-11-01T04:12:31.000Z",
  "periodEnd": "2026-12-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Gym membership, every month",
      "quantity": 1,
      "unitAmountCents": 450000,
      "amountCents": 450000,
      "periodStart": "2026-11-01T04:12:31.000Z",
      "periodEnd": "2026-12-01T04:12:31.000Z",
      "proration": false
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 450000,
  "creditAppliedCents": 0,
  "amountDueCents": 450000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 0,
  "nextAttemptAt": null,
  "lastFailure": null,
  "paidAt": null,
  "hostedInvoiceUrl": null,
  "createdAt": "2026-10-29T04:13:00.000Z"
}

Query parameters

NameDescription
subscriptionIdstringrequiredThe subscription whose next invoice to preview.

Returns 200 with an Invoice object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Preview a change

post/v1/invoices/preview

The invoice a change to a subscription would make, without making it: with always_invoice (or a change of interval) the invoice made at once, with its proration lines; otherwise the next renewal with the new items and the prorations it would carry. Send the same prorationDate with the update so the amounts match. Nothing is written, and the preview has no id of its own. A read: no Idempotency-Key.

Key
A secret key (permission billing:read)
Idempotency-Key
Not used
Preview a change: request
curl https://api.payments.lk/v1/invoices/preview \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "priceId": "price_w7k2x9c4q1m8v3r6t0ynhz5p",
    "prorationBehavior": "always_invoice",
    "prorationDate": "2026-10-16T04:12:31.000Z"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "upcoming_sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "mode": "test",
  "number": null,
  "status": "draft",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_update",
  "periodStart": "2026-10-16T04:12:31.000Z",
  "periodEnd": "2026-11-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Unused time on Gym membership from 16 October",
      "quantity": 1,
      "unitAmountCents": -239063,
      "amountCents": -239063,
      "periodStart": "2026-10-16T04:12:31.000Z",
      "periodEnd": "2026-11-01T04:12:31.000Z",
      "proration": true
    },
    {
      "description": "Remaining time on Gym and pool from 16 October",
      "quantity": 1,
      "unitAmountCents": 318750,
      "amountCents": 318750,
      "periodStart": "2026-10-16T04:12:31.000Z",
      "periodEnd": "2026-11-01T04:12:31.000Z",
      "proration": true
    }
  ],
  "subtotalCents": 79687,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 79687,
  "creditAppliedCents": 0,
  "amountDueCents": 79687,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 0,
  "nextAttemptAt": null,
  "lastFailure": null,
  "paidAt": null,
  "hostedInvoiceUrl": null,
  "createdAt": "2026-10-16T04:12:31.000Z"
}

Body

FieldDescription
subscriptionIdstringrequiredThe subscription the change is for.
itemsarray of objectThe item changes to preview, as the update takes them.
items[].idstringThe item to change, from the subscription's items.
items[].priceIdstringIts new price, or the price of a new item.
items[].quantityintegerIts new quantity, 1 to 10000.1 to 10000
items[].deleted"true"True removes the item.
priceIdstringFor a subscription of one item: its new price.
quantityintegerFor a subscription of one item: its new quantity, 1 to 10000.1 to 10000
prorationBehaviorone of: create_prorations, always_invoice, nonecreate_prorations (the default), always_invoice or none, as the update will send it.
prorationDatestring, ISO 8601 timePrice the change as at this time, ISO 8601; defaults to now. Send the same time with the update.

Returns 200 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The proration date is outside the period, or the items would not go together.json_requiredinvalid_jsonbilling_date_invaliditems_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
409CONFLICTThe subscription has ended, or a price is archived or in the other mode.subscription_endedprice_unavailable
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Send an invoice again

post/v1/invoices/{id}/send

Emails an open invoice collected by sending it to the customer again, with its page to pay on. invoice.sent is sent. Reminders go out by themselves three days before the due date and when it passes.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Send an invoice again: request
curl -X POST https://api.payments.lk/v1/invoices/in_s5v1x8c3q9m2k7r4t0yhwn6z/send \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: term-3-nimali-resend"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "in_s5v1x8c3q9m2k7r4t0yhwn6z",
  "mode": "test",
  "number": "INV-000014",
  "status": "open",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": null,
  "billingReason": "manual",
  "periodStart": null,
  "periodEnd": null,
  "lines": [
    {
      "description": "Grade 10 maths, third term",
      "quantity": 1,
      "unitAmountCents": 1800000,
      "amountCents": 1800000,
      "periodStart": null,
      "periodEnd": null,
      "proration": false
    }
  ],
  "subtotalCents": 1800000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 1800000,
  "creditAppliedCents": 0,
  "amountDueCents": 1800000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "send_invoice",
  "dueDate": "2026-10-31T18:29:59.000Z",
  "memo": "Third term, grade 10",
  "currency": "LKR",
  "attemptCount": 0,
  "nextAttemptAt": null,
  "lastFailure": null,
  "paidAt": null,
  "hostedInvoiceUrl": "https://payments.lk/invoice/r2Vd9x4Kc7mZt3Wn1Rb8Hy6Lp5Gs0Jf4Ae2Uo9Ik7Nx",
  "createdAt": "2026-10-01T08:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe invoice's id, in_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is not open, or it is charged to the card on file rather than sent.idempotency_key_reusedidempotency_key_in_progressinvoice_not_open
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a credit note

post/v1/credit_notes

Takes an amount off an invoice, as Stripe's credit notes. On an open invoice it lowers what is due (all of it pays the invoice). On a paid one it is refunded to the card the invoice was paid with (refund true), or added to the customer's credit balance for their next invoices. Numbered CN-000001 in your own sequence; credit_note.created is sent.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a credit note: request
curl https://api.payments.lk/v1/credit_notes \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cn-october-closure" \
  -d '{
    "invoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "amountCents": 150000,
    "reason": "order_change",
    "memo": "Closed for a week in October"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "credit_note",
  "id": "cn_r7v2x9c4q1m8k3t6y0wnhz5p",
  "mode": "test",
  "invoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "number": "CN-000001",
  "amountCents": 150000,
  "reason": "order_change",
  "memo": "Closed for a week in October",
  "amountDueReducedCents": 0,
  "refundedCents": 0,
  "creditedToBalanceCents": 150000,
  "refundId": null,
  "createdAt": "2026-10-20T08:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
invoiceIdstringrequiredThe invoice, open or paid.
amountCentsintegerrequiredHow much, in cents: at most what is due on an open invoice, or what was paid and not yet credited on a paid one.1 to 100000000
reasonone of: duplicate, fraudulent, order_change, product_unsatisfactoryduplicate, fraudulent, order_change or product_unsatisfactory.
memostringA note, up to 500 characters.at most 500 characters
refundbooleanOn a paid invoice: true refunds the amount to the card it was paid with; false (the default) adds it to the customer's credit balance for their next invoices.default false

Returns 201 with a CreditNote object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. More than the invoice still has to give.json_requiredinvalid_jsonidempotency_key_missingamount
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is neither open nor paid, or it was not paid by card here and a refund was asked for.idempotency_key_reusedidempotency_key_in_progressinvoice_not_opennot_refundable
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List credit notes

get/v1/credit_notes

Your credit notes in this key's mode, newest first. Filter by invoice.

Key
A secret key (permission billing:read)
List credit notes: request
curl "https://api.payments.lk/v1/credit_notes?invoiceId=in_c4v9x2q7m1k8r3t6y0wnzhp5" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "credit_note",
      "id": "cn_r7v2x9c4q1m8k3t6y0wnhz5p",
      "mode": "test",
      "invoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "number": "CN-000001",
      "amountCents": 150000,
      "reason": "order_change",
      "memo": "Closed for a week in October",
      "amountDueReducedCents": 0,
      "refundedCents": 0,
      "creditedToBalanceCents": 150000,
      "refundId": null,
      "createdAt": "2026-10-20T08:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
invoiceIdstringOnly this invoice's credit notes.

Returns 200 with a list of CreditNote objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a credit note

get/v1/credit_notes/{id}

One credit note.

Key
A secret key (permission billing:read)
Retrieve a credit note: request
curl https://api.payments.lk/v1/credit_notes/cn_r7v2x9c4q1m8k3t6y0wnhz5p \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "credit_note",
  "id": "cn_r7v2x9c4q1m8k3t6y0wnhz5p",
  "mode": "test",
  "invoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "number": "CN-000001",
  "amountCents": 150000,
  "reason": "order_change",
  "memo": "Closed for a week in October",
  "amountDueReducedCents": 0,
  "refundedCents": 0,
  "creditedToBalanceCents": 150000,
  "refundId": null,
  "createdAt": "2026-10-20T08:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe credit note's id, cn_ followed by 24 characters.

Returns 200 with a CreditNote object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo credit note with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Charge an invoice now

post/v1/invoices/{id}/pay

Charges an open invoice to the card on file (the subscription's, or the customer's default) on the worker's next pass, within a minute, whatever the retry schedule says. The outcome arrives as invoice.paid or invoice.payment_failed. A customer without a card pays on the invoice's hostedInvoiceUrl instead.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Charge an invoice now: request
curl -X POST https://api.payments.lk/v1/invoices/in_x7c2v9q4m1k6r8t3y0hwnzp5/pay \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: pay-inv-000002"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
  "mode": "test",
  "number": "INV-000002",
  "status": "open",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_cycle",
  "periodStart": "2026-11-01T04:12:31.000Z",
  "periodEnd": "2026-12-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Gym membership, every month",
      "quantity": 1,
      "unitAmountCents": 450000,
      "amountCents": 450000,
      "periodStart": "2026-11-01T04:12:31.000Z",
      "periodEnd": "2026-12-01T04:12:31.000Z",
      "proration": false
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 450000,
  "creditAppliedCents": 0,
  "amountDueCents": 450000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 1,
  "nextAttemptAt": "2026-11-01T09:40:00.000Z",
  "lastFailure": "The processor refused the charge.",
  "paidAt": null,
  "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
  "createdAt": "2026-11-01T04:13:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe invoice's id, in_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is not open, a charge of it is still with the processor, or there is no card to charge.idempotency_key_reusedidempotency_key_in_progressinvoice_not_openinvoice_payment_pendingno_card_on_file
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Finalise a draft now

post/v1/invoices/{id}/finalize

Ends a renewal's draft window early: the invoice is numbered, gets its hosted page, and is charged to the card on file on the worker's next pass (an unpaid subscription's invoice is left for the customer to pay).

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Finalise a draft now: request
curl -X POST https://api.payments.lk/v1/invoices/in_x7c2v9q4m1k6r8t3y0hwnzp5/finalize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: finalize-inv-draft"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
  "mode": "test",
  "number": "INV-000002",
  "status": "open",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_cycle",
  "periodStart": "2026-11-01T04:12:31.000Z",
  "periodEnd": "2026-12-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Gym membership, every month",
      "quantity": 1,
      "unitAmountCents": 450000,
      "amountCents": 450000,
      "periodStart": "2026-11-01T04:12:31.000Z",
      "periodEnd": "2026-12-01T04:12:31.000Z",
      "proration": false
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 450000,
  "creditAppliedCents": 0,
  "amountDueCents": 450000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 0,
  "nextAttemptAt": "2026-11-01T04:20:00.000Z",
  "lastFailure": null,
  "paidAt": null,
  "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
  "createdAt": "2026-11-01T04:13:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe invoice's id, in_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is not a draft.idempotency_key_reusedidempotency_key_in_progressinvoice_not_open
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Void an invoice

post/v1/invoices/{id}/void

Cancels a draft or open invoice: nothing is owed on it and nothing more is tried. A past due or unpaid subscription with nothing else open is active again. Voiding a subscription's first invoice before it is paid ends the subscription as incomplete_expired.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Void an invoice: request
curl -X POST https://api.payments.lk/v1/invoices/in_x7c2v9q4m1k6r8t3y0hwnzp5/void \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: void-inv-000002"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
  "mode": "test",
  "number": "INV-000002",
  "status": "void",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_cycle",
  "periodStart": "2026-11-01T04:12:31.000Z",
  "periodEnd": "2026-12-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Gym membership, every month",
      "quantity": 1,
      "unitAmountCents": 450000,
      "amountCents": 450000,
      "periodStart": "2026-11-01T04:12:31.000Z",
      "periodEnd": "2026-12-01T04:12:31.000Z",
      "proration": false
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 450000,
  "creditAppliedCents": 0,
  "amountDueCents": 450000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 1,
  "nextAttemptAt": null,
  "lastFailure": "The processor refused the charge.",
  "paidAt": null,
  "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
  "createdAt": "2026-11-01T04:13:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe invoice's id, in_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is paid, void or uncollectible already, or a charge of it is still with the processor.idempotency_key_reusedidempotency_key_in_progressinvoice_not_openinvoice_payment_pending
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Write an invoice off

post/v1/invoices/{id}/mark_uncollectible

Marks an open invoice uncollectible: it was owed, and will never be tried again. A past due or unpaid subscription with nothing else open is active again.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Write an invoice off: request
curl -X POST https://api.payments.lk/v1/invoices/in_x7c2v9q4m1k6r8t3y0hwnzp5/mark_uncollectible \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: writeoff-inv-000002"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice",
  "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
  "mode": "test",
  "number": "INV-000002",
  "status": "uncollectible",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "billingReason": "subscription_cycle",
  "periodStart": "2026-11-01T04:12:31.000Z",
  "periodEnd": "2026-12-01T04:12:31.000Z",
  "lines": [
    {
      "description": "Gym membership, every month",
      "quantity": 1,
      "unitAmountCents": 450000,
      "amountCents": 450000,
      "periodStart": "2026-11-01T04:12:31.000Z",
      "periodEnd": "2026-12-01T04:12:31.000Z",
      "proration": false
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "couponId": null,
  "taxCents": 0,
  "taxes": [],
  "totalCents": 450000,
  "creditAppliedCents": 0,
  "amountDueCents": 450000,
  "amountPaidCents": 0,
  "creditNotesCents": 0,
  "collectionMethod": "charge_automatically",
  "dueDate": null,
  "memo": null,
  "currency": "LKR",
  "attemptCount": 1,
  "nextAttemptAt": null,
  "lastFailure": "The processor refused the charge.",
  "paidAt": null,
  "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
  "createdAt": "2026-11-01T04:13:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe invoice's id, in_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with an Invoice object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is not open, or a charge of it is still with the processor.idempotency_key_reusedidempotency_key_in_progressinvoice_not_openinvoice_payment_pending
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a coupon

post/v1/coupons

A discount for subscriptions: percent or amount off, for their next invoice (once), some months (repeating) or for as long as they run (forever). Put it on a subscription with couponId.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a coupon: request
curl https://api.payments.lk/v1/coupons \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: coupon-new-year-2027" \
  -d '{
    "name": "New year, 20% off for 3 months",
    "percentOffBps": 2000,
    "duration": "repeating",
    "durationInMonths": 3,
    "maxRedemptions": 100,
    "redeemBy": "2027-01-31T18:30:00.000Z"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "coupon",
  "id": "cpn_n5v2x8c1q7m3k9r4t0yhwz6p",
  "mode": "test",
  "name": "New year, 20% off for 3 months",
  "percentOffBps": 2000,
  "amountOffCents": null,
  "duration": "repeating",
  "durationInMonths": 3,
  "maxRedemptions": 100,
  "timesRedeemed": 0,
  "redeemBy": "2027-01-31T18:30:00.000Z",
  "active": true,
  "code": "NEWYEAR20",
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
namestringrequiredIts name, 1 to 80 characters.1 to 80 characters
percentOffBpsintegerBasis points off, 1 to 10000 (2000 is 20%). Send this or amountOffCents.1 to 10000
amountOffCentsintegerCents off each invoice it discounts, from 100. Send this or percentOffBps.100 to 100000000
durationone of: once, repeating, foreverrequiredonce, repeating or forever.
durationInMonthsintegerFor repeating: how many months, 1 to 36.1 to 36
maxRedemptionsintegerHow many subscriptions may take it.1 to 1000000
redeemBystring, ISO 8601 timeThe last time a subscription may take it, ISO 8601.
codestringA code customers type on a subscription link, or in the customer portal where you allow it, to take this coupon: 3 to 24 letters, digits, dashes or underscores, kept in upper case, unique in the mode.

Returns 201 with a Coupon object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. redeemBy has passed.json_requiredinvalid_jsonidempotency_key_missingbilling_date_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List coupons

get/v1/coupons

Your coupons in this key's mode, newest first.

Key
A secret key (permission billing:read)
List coupons: request
curl "https://api.payments.lk/v1/coupons?active=true" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "coupon",
      "id": "cpn_n5v2x8c1q7m3k9r4t0yhwz6p",
      "mode": "test",
      "name": "New year, 20% off for 3 months",
      "percentOffBps": 2000,
      "amountOffCents": null,
      "duration": "repeating",
      "durationInMonths": 3,
      "maxRedemptions": 100,
      "timesRedeemed": 1,
      "redeemBy": "2027-01-31T18:30:00.000Z",
      "active": true,
      "code": "NEWYEAR20",
      "createdAt": "2026-09-25T08:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
activeone of: true, falsetrue for coupons taking new subscriptions, false for archived ones.

Returns 200 with a list of Coupon objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a coupon

get/v1/coupons/{id}

One coupon, with how many subscriptions have taken it.

Key
A secret key (permission billing:read)
Retrieve a coupon: request
curl https://api.payments.lk/v1/coupons/cpn_n5v2x8c1q7m3k9r4t0yhwz6p \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "coupon",
  "id": "cpn_n5v2x8c1q7m3k9r4t0yhwz6p",
  "mode": "test",
  "name": "New year, 20% off for 3 months",
  "percentOffBps": 2000,
  "amountOffCents": null,
  "duration": "repeating",
  "durationInMonths": 3,
  "maxRedemptions": 100,
  "timesRedeemed": 1,
  "redeemBy": "2027-01-31T18:30:00.000Z",
  "active": true,
  "code": "NEWYEAR20",
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe coupon's id, cpn_ followed by 24 characters.

Returns 200 with a Coupon object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo coupon with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Archive a coupon

delete/v1/coupons/{id}

Stops new subscriptions taking it. Subscriptions that have it keep their discount, as Stripe's do.

Key
A secret key (permission billing:write)
Idempotency-Key
Not used
Archive a coupon: request
curl -X DELETE https://api.payments.lk/v1/coupons/cpn_n5v2x8c1q7m3k9r4t0yhwz6p \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "coupon",
  "id": "cpn_n5v2x8c1q7m3k9r4t0yhwz6p",
  "mode": "test",
  "name": "New year, 20% off for 3 months",
  "percentOffBps": 2000,
  "amountOffCents": null,
  "duration": "repeating",
  "durationInMonths": 3,
  "maxRedemptions": 100,
  "timesRedeemed": 1,
  "redeemBy": "2027-01-31T18:30:00.000Z",
  "active": false,
  "code": "NEWYEAR20",
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe coupon's id, cpn_ followed by 24 characters.

Returns 200 with a Coupon object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo coupon with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a tax rate

post/v1/tax_rates

A tax you charge on invoices, such as VAT 18% or SSCL 2.5%, added on top or included in your prices. Put it on a subscription with taxRateIds.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a tax rate: request
curl https://api.payments.lk/v1/tax_rates \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: tax-vat-18" \
  -d '{
    "displayName": "VAT",
    "description": "Value added tax",
    "percentageBps": 1800,
    "inclusive": false
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "tax_rate",
  "id": "txr_v1x8c3q6m2k9r5t7y0wnhz4p",
  "mode": "test",
  "displayName": "VAT",
  "description": "Value added tax",
  "percentageBps": 1800,
  "inclusive": false,
  "active": true,
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
displayNamestringrequiredIts name on invoices, 1 to 40 characters, such as VAT.1 to 40 characters
descriptionstringA line about it, up to 120 characters.at most 120 characters
percentageBpsintegerrequiredThe rate in basis points, 0 to 5000: 1800 is 18%.0 to 5000
inclusivebooleanTrue when your prices already include it. Defaults to false: added on top.default false

Returns 201 with a TaxRate object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List tax rates

get/v1/tax_rates

Your tax rates in this key's mode, newest first.

Key
A secret key (permission billing:read)
List tax rates: request
curl "https://api.payments.lk/v1/tax_rates?active=true" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "tax_rate",
      "id": "txr_v1x8c3q6m2k9r5t7y0wnhz4p",
      "mode": "test",
      "displayName": "VAT",
      "description": "Value added tax",
      "percentageBps": 1800,
      "inclusive": false,
      "active": true,
      "createdAt": "2026-09-25T08:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
activeone of: true, falsetrue for rates taking new subscriptions, false for archived ones.

Returns 200 with a list of TaxRate objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a tax rate

get/v1/tax_rates/{id}

One tax rate.

Key
A secret key (permission billing:read)
Retrieve a tax rate: request
curl https://api.payments.lk/v1/tax_rates/txr_v1x8c3q6m2k9r5t7y0wnhz4p \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "tax_rate",
  "id": "txr_v1x8c3q6m2k9r5t7y0wnhz4p",
  "mode": "test",
  "displayName": "VAT",
  "description": "Value added tax",
  "percentageBps": 1800,
  "inclusive": false,
  "active": true,
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe tax rate's id, txr_ followed by 24 characters.

Returns 200 with a TaxRate object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo tax rate with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Update a tax rate

post/v1/tax_rates/{id}

Its name or description, or active false to archive it: no new subscription takes it, those that have it keep it. Its percentage never changes; for a new rate, create a new tax rate.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Update a tax rate: request
curl https://api.payments.lk/v1/tax_rates/txr_v1x8c3q6m2k9r5t7y0wnhz4p \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: archive-vat-18" \
  -d '{
    "active": false
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "tax_rate",
  "id": "txr_v1x8c3q6m2k9r5t7y0wnhz4p",
  "mode": "test",
  "displayName": "VAT",
  "description": "Value added tax",
  "percentageBps": 1800,
  "inclusive": false,
  "active": false,
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe tax rate's id, txr_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
displayNamestringA new name.1 to 40 characters
descriptionstring or nullA new line about it, or null.at most 120 characters
activebooleanFalse archives it: no new subscription takes it.

Returns 200 with a TaxRate object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo tax rate with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Add an invoice item

post/v1/invoice_items

A one-off line, such as a setup fee or an add-on (a negative amount is a credit): it waits for the customer's next invoice, or the named subscription's, and joins a draft at once when you name one during its draft window.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Add an invoice item: request
curl https://api.payments.lk/v1/invoice_items \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: joining-fee-member-1042" \
  -d '{
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "description": "Joining fee",
    "unitAmountCents": 250000,
    "discountable": false
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "invoice_item",
  "id": "ii_f3v7x2c9q1m8k4r6t0yhzw5n",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "invoiceId": null,
  "description": "Joining fee",
  "quantity": 1,
  "unitAmountCents": 250000,
  "amountCents": 250000,
  "proration": false,
  "discountable": false,
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
descriptionstringrequiredThe line as the customer reads it, 1 to 200 characters.1 to 200 characters
unitAmountCentsintegerrequiredPer unit, in cents; negative for a credit.-100000000 to 100000000
quantityintegerHow many units, 1 to 10000. Defaults to 1.1 to 10000; default 1
discountablebooleanWhether a coupon discounts it. Defaults to true.default true
customerIdstringrequiredThe customer it bills.
subscriptionIdstringOnly this subscription's next invoice takes it. Without it, the customer's next invoice does.
invoiceIdstringA draft invoice to add it to now, during its draft window.

Returns 201 with an InvoiceItem object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer, subscription or invoice with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The invoice is not a draft, or the subscription has ended.idempotency_key_reusedidempotency_key_in_progressinvoice_not_opensubscription_ended
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List invoice items

get/v1/invoice_items

Your invoice items in this key's mode, newest first. Filter by customer, and by whether an invoice has taken them.

Key
A secret key (permission billing:read)
List invoice items: request
curl "https://api.payments.lk/v1/invoice_items?customerId=cus_q8w3n5v1x7c2m9r4t6ya0kzp&pending=true" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "invoice_item",
      "id": "ii_f3v7x2c9q1m8k4r6t0yhzw5n",
      "mode": "test",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
      "invoiceId": null,
      "description": "Joining fee",
      "quantity": 1,
      "unitAmountCents": 250000,
      "amountCents": 250000,
      "proration": false,
      "discountable": false,
      "createdAt": "2026-10-01T04:12:31.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
customerIdstringOnly this customer's items.
pendingone of: true, falsetrue for items no invoice has taken yet, false for the ones taken.

Returns 200 with a list of InvoiceItem objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Delete an invoice item

delete/v1/invoice_items/{id}

Deletes an item no invoice has taken yet. One an invoice took stays as that invoice's record.

Key
A secret key (permission billing:write)
Idempotency-Key
Not used
Delete an invoice item: request
curl -X DELETE https://api.payments.lk/v1/invoice_items/ii_f3v7x2c9q1m8k4r6t0yhzw5n \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "invoice_item",
  "id": "ii_f3v7x2c9q1m8k4r6t0yhzw5n",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "invoiceId": null,
  "description": "Joining fee",
  "quantity": 1,
  "unitAmountCents": 250000,
  "amountCents": 250000,
  "proration": false,
  "discountable": false,
  "createdAt": "2026-10-01T04:12:31.000Z",
  "deleted": true
}

Path parameters

NameDescription
idstringrequiredThe invoice item's id, ii_ followed by 24 characters.

Returns 200 with a DeletedInvoiceItem object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo invoice item with this id for this key; mode_mismatch when it exists in the other mode.mode_mismatch
409CONFLICTAn invoice has taken it.invoice_not_open
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Adjust a customer's credit

post/v1/customers/{id}/balance_transactions

Adds credit to the customer's balance (positive) or takes it away (negative, never below zero). Credit is taken off their next invoices before the card is charged.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Adjust a customer's credit: request
curl https://api.payments.lk/v1/customers/cus_q8w3n5v1x7c2m9r4t6ya0kzp/balance_transactions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: credit-pool-week" \
  -d '{
    "amountCents": 150000,
    "description": "Goodwill credit for the closed pool week"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "customer_balance_transaction",
  "id": "cbtx_b4x9v2c7q3m1k8r5t0nwhz6y",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "amountCents": 150000,
  "endingBalanceCents": 150000,
  "type": "adjustment",
  "invoiceId": null,
  "description": "Goodwill credit for the closed pool week",
  "createdAt": "2026-10-12T09:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe customer's id, cus_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
amountCentsintegerrequiredPositive adds credit to the customer's balance; negative takes it away, never below zero.-100000000 to 100000000
descriptionstringWhy, up to 200 characters.at most 200 characters

Returns 201 with a CustomerBalanceTransaction object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. It would take the balance below zero.json_requiredinvalid_jsonidempotency_key_missingamount
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List a customer's credit changes

get/v1/customers/{id}/balance_transactions

Every change to the customer's credit balance, newest first, each with the balance after it.

Key
A secret key (permission billing:read)
List a customer's credit changes: request
curl https://api.payments.lk/v1/customers/cus_q8w3n5v1x7c2m9r4t6ya0kzp/balance_transactions \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "customer_balance_transaction",
      "id": "cbtx_b4x9v2c7q3m1k8r5t0nwhz6y",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "amountCents": 150000,
      "endingBalanceCents": 150000,
      "type": "adjustment",
      "invoiceId": null,
      "description": "Goodwill credit for the closed pool week",
      "createdAt": "2026-10-12T09:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Path parameters

NameDescription
idstringrequiredThe customer's id, cus_ followed by 24 characters.

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.

Returns 200 with a list of CustomerBalanceTransaction objects.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Move a subscription's time forward

post/v1/test_clocks/advance

Sandbox only. Runs one subscription's clock forward by up to a year, so you can watch the upcoming notice, the renewal, the charge, a retry and a cancellation in minutes instead of months. The worker catches up within a minute of each call.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Move a subscription's time forward: request
curl https://api.payments.lk/v1/test_clocks/advance \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: clock-member-1042-1" \
  -d '{
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "seconds": 2678400
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "test_clock",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "frozenTime": "2026-11-01T04:20:00.000Z",
  "offsetSeconds": 2678400
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
subscriptionIdstringrequiredA sandbox subscription.
secondsintegerrequiredHow far to move its clock forward: 60 seconds to a year.60 to 31622400

Returns 200 with a TestClock object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. A live key where real money moves, or a subscription that has ended.idempotency_key_reusedidempotency_key_in_progresstest_clock_unavailablesubscription_ended
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a subscription schedule

post/v1/subscription_schedules

Phases a subscription moves through, each its prices for a number of billing periods: a discounted first year and then the full price, or installments. The subscription starts on startDate (now by default), charged as any new subscription is: the card on file, or the first invoice's hostedInvoiceUrl. At the renewal where a phase ends it moves to the next phase's prices, with no proration. After the last phase it carries on (endBehavior release) or ends (cancel). Installments: one phase of the installment price with iterations set to the number of payments, and endBehavior cancel.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a subscription schedule: request
curl https://api.payments.lk/v1/subscription_schedules \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: diploma-nimali-installments" \
  -d '{
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "startDate": "now",
    "endBehavior": "cancel",
    "phases": [
      {
        "items": [
          {
            "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
            "quantity": 1
          }
        ],
        "iterations": 12
      }
    ],
    "reference": "Diploma, 12 installments"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "subscription_schedule",
  "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "status": "active",
  "endBehavior": "cancel",
  "startDate": "2026-10-01T04:12:31.000Z",
  "phases": [
    {
      "items": [
        {
          "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
          "quantity": 1
        }
      ],
      "iterations": 12,
      "trialDays": null,
      "couponId": null
    }
  ],
  "currentPhase": 0,
  "currentPhaseEndsAt": "2027-10-01T04:12:31.000Z",
  "reference": "Diploma, 12 installments",
  "completedAt": null,
  "releasedAt": null,
  "canceledAt": null,
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
customerIdstringrequiredThe customer, one of yours in this key's mode.
startDate"now" or string, ISO 8601 time"now" (the default) starts the subscription at once, charged as any new subscription is; a time within two years starts it then.default "now"
endBehaviorone of: release, cancelAfter the last phase: release (the default: the subscription carries on at the last phase's prices) or cancel (it ends: installments).default "release"
phasesarray of objectrequired1 to 10 phases, in order.
phases[].itemsarray of objectrequiredThe phase's prices, 1 to 10, each once, all on one interval.
phases[].items[].priceIdstringrequiredAn active price of yours.
phases[].items[].quantityintegerHow many units, 1 to 10000. Defaults to 1.1 to 10000; default 1
phases[].iterationsintegerHow many billing periods the phase lasts, 1 to 120. Every phase but the last has it; give the last one too for endBehavior cancel.1 to 120
phases[].trialDaysintegerA free trial at the start of the first phase, 1 to 730 days.1 to 730
phases[].couponIdstringA coupon of yours put on the subscription when the phase starts.
referencestringYour own reference, up to 120 characters.at most 120 characters

Returns 201 with a SubscriptionSchedule object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. A phase's prices bill on different intervals, or the start date is out of range.json_requiredinvalid_jsonidempotency_key_missingitems_invalidbilling_date_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. A price is archived or in the other mode, or a coupon cannot be used.idempotency_key_reusedidempotency_key_in_progressprice_unavailablecoupon_invalid
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List subscription schedules

get/v1/subscription_schedules

Your subscription schedules in this key's mode, newest first. Filter by customer and status.

Key
A secret key (permission billing:read)
List subscription schedules: request
curl "https://api.payments.lk/v1/subscription_schedules?status=active" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "subscription_schedule",
      "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
      "mode": "test",
      "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
      "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
      "status": "active",
      "endBehavior": "cancel",
      "startDate": "2026-10-01T04:12:31.000Z",
      "phases": [
        {
          "items": [
            {
              "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
              "quantity": 1
            }
          ],
          "iterations": 12,
          "trialDays": null,
          "couponId": null
        }
      ],
      "currentPhase": 0,
      "currentPhaseEndsAt": "2027-10-01T04:12:31.000Z",
      "reference": "Diploma, 12 installments",
      "completedAt": null,
      "releasedAt": null,
      "canceledAt": null,
      "createdAt": "2026-10-01T04:12:31.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
customerIdstringOnly this customer's schedules.
statusone of: not_started, active, completed, released, canceledOnly schedules with this status.

Returns 200 with a list of SubscriptionSchedule objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a subscription schedule

get/v1/subscription_schedules/{id}

One schedule, with the phase now running and when it ends.

Key
A secret key (permission billing:read)
Retrieve a subscription schedule: request
curl https://api.payments.lk/v1/subscription_schedules/sched_c6v2x9q1m8k4r7t3y0hwnz5p \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription_schedule",
  "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "status": "active",
  "endBehavior": "cancel",
  "startDate": "2026-10-01T04:12:31.000Z",
  "phases": [
    {
      "items": [
        {
          "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
          "quantity": 1
        }
      ],
      "iterations": 12,
      "trialDays": null,
      "couponId": null
    }
  ],
  "currentPhase": 0,
  "currentPhaseEndsAt": "2027-10-01T04:12:31.000Z",
  "reference": "Diploma, 12 installments",
  "completedAt": null,
  "releasedAt": null,
  "canceledAt": null,
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription schedule's id, sched_ followed by 24 characters.

Returns 200 with a SubscriptionSchedule object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription schedule with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Cancel a subscription schedule

post/v1/subscription_schedules/{id}/cancel

A schedule not started never starts; one running cancels its subscription now, as a cancellation from the API does.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Cancel a subscription schedule: request
curl -X POST https://api.payments.lk/v1/subscription_schedules/sched_c6v2x9q1m8k4r7t3y0hwnz5p/cancel \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: diploma-nimali-cancel"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription_schedule",
  "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "status": "canceled",
  "endBehavior": "cancel",
  "startDate": "2026-10-01T04:12:31.000Z",
  "phases": [
    {
      "items": [
        {
          "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
          "quantity": 1
        }
      ],
      "iterations": 12,
      "trialDays": null,
      "couponId": null
    }
  ],
  "currentPhase": 0,
  "currentPhaseEndsAt": null,
  "reference": "Diploma, 12 installments",
  "completedAt": null,
  "releasedAt": null,
  "canceledAt": "2027-02-01T04:12:31.000Z",
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription schedule's id, sched_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with a SubscriptionSchedule object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription schedule with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The schedule has already finished.idempotency_key_reusedidempotency_key_in_progresssubscription_change_refused
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Release a subscription schedule

post/v1/subscription_schedules/{id}/release

Lets go of the subscription, which carries on at the prices it has now, with no more phases. A schedule not started never starts.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Release a subscription schedule: request
curl -X POST https://api.payments.lk/v1/subscription_schedules/sched_c6v2x9q1m8k4r7t3y0hwnz5p/release \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: diploma-nimali-release"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "subscription_schedule",
  "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
  "status": "released",
  "endBehavior": "cancel",
  "startDate": "2026-10-01T04:12:31.000Z",
  "phases": [
    {
      "items": [
        {
          "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
          "quantity": 1
        }
      ],
      "iterations": 12,
      "trialDays": null,
      "couponId": null
    }
  ],
  "currentPhase": 0,
  "currentPhaseEndsAt": null,
  "reference": "Diploma, 12 installments",
  "completedAt": null,
  "releasedAt": "2027-02-01T04:12:31.000Z",
  "canceledAt": null,
  "createdAt": "2026-10-01T04:12:31.000Z"
}

Path parameters

NameDescription
idstringrequiredThe subscription schedule's id, sched_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with a SubscriptionSchedule object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo subscription schedule with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. The schedule has already finished.idempotency_key_reusedidempotency_key_in_progresssubscription_change_refused
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a feature

post/v1/entitlements/features

A feature customers get with a product, as Stripe's entitlements: give it to products with POST /v1/products/{id}/features, and check a customer's features in your app by lookup key. entitlements.active_entitlement_summary.updated tells you when a customer's features change.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a feature: request
curl https://api.payments.lk/v1/entitlements/features \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: feature-pool" \
  -d '{
    "name": "Swimming pool",
    "lookupKey": "pool"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "entitlements.feature",
  "id": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
  "mode": "test",
  "name": "Swimming pool",
  "lookupKey": "pool",
  "active": true,
  "createdAt": "2026-09-25T08:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
namestringrequiredIts name, 1 to 80 characters.1 to 80 characters
lookupKeystringrequiredThe key your app checks: lower case letters, digits, dots, dashes and underscores, up to 80, unique in the mode.

Returns 201 with a Feature object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. Another feature in this mode already has that lookup key.idempotency_key_reusedidempotency_key_in_progressfeature_exists
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List features

get/v1/entitlements/features

Your features in this key's mode, newest first.

Key
A secret key (permission billing:read)
List features: request
curl https://api.payments.lk/v1/entitlements/features \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "entitlements.feature",
      "id": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
      "mode": "test",
      "name": "Swimming pool",
      "lookupKey": "pool",
      "active": true,
      "createdAt": "2026-09-25T08:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.

Returns 200 with a list of Feature objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List a customer's features

get/v1/entitlements/active_entitlements

The features a customer has now: those of the products on their active, trialing and past due subscriptions, each once. Check one by lookup key before letting them use it.

Key
A secret key (permission billing:read)
List a customer's features: request
curl "https://api.payments.lk/v1/entitlements/active_entitlements?customerId=cus_q8w3n5v1x7c2m9r4t6ya0kzp" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "entitlements.active_entitlement",
      "featureId": "feat_g2v7x9c5q1m8k3r6t0hwnz4y",
      "lookupKey": "gym"
    },
    {
      "object": "entitlements.active_entitlement",
      "featureId": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
      "lookupKey": "pool"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
customerIdstringrequiredThe customer.

Returns 200 with a list of ActiveEntitlement objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Give a product a feature

post/v1/products/{id}/features

Every running subscription to the product now gives its customer the feature; their summaries are sent. A feature already on the product is answered as it is.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Give a product a feature: request
curl https://api.payments.lk/v1/products/prod_h2k9m4q7v1x8c3r6t0ynwz5p/features \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: gym-pool-feature" \
  -d '{
    "featureId": "feat_p3v8x1c6q9m2k7r4t0ynhw5z"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "product_feature",
  "id": "pfeat_q4v9x2c7m1k8r3t6y0wnhz5p",
  "productId": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
  "featureId": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
  "lookupKey": "pool",
  "createdAt": "2026-09-25T08:05:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe product's id, prod_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
featureIdstringrequiredThe feature to give with the product.

Returns 200 with a ProductFeature object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo product with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List a product's features

get/v1/products/{id}/features

The features given with a product, oldest first.

Key
A secret key (permission billing:read)
List a product's features: request
curl https://api.payments.lk/v1/products/prod_h2k9m4q7v1x8c3r6t0ynwz5p/features \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "product_feature",
      "id": "pfeat_q4v9x2c7m1k8r3t6y0wnhz5p",
      "productId": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
      "featureId": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
      "lookupKey": "pool",
      "createdAt": "2026-09-25T08:05:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Path parameters

NameDescription
idstringrequiredThe product's id, prod_ followed by 24 characters.

Returns 200 with a list of ProductFeature objects.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo product with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Take a feature off a product

delete/v1/products/{id}/features/{featureId}

Customers with the product lose the feature, unless another of their products gives it; their summaries are sent.

Key
A secret key (permission billing:write)
Idempotency-Key
Not used
Take a feature off a product: request
curl -X DELETE https://api.payments.lk/v1/products/prod_h2k9m4q7v1x8c3r6t0ynwz5p/features/feat_p3v8x1c6q9m2k7r4t0ynhw5z \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "product_feature",
  "productId": "prod_h2k9m4q7v1x8c3r6t0ynwz5p",
  "featureId": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
  "deleted": true
}

Path parameters

NameDescription
idstringrequiredThe product's id, prod_ followed by 24 characters.
featureIdstringrequiredThe feature's id, feat_ followed by 24 characters.

Returns 200 with a DeletedProductFeature object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo product feature with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a meter

post/v1/meters

What you count for usage based billing: API calls, messages, kilowatt hours. Report each use with POST /v1/meter_events under its eventName; a metered price on the meter bills a customer's usage at the end of each period, by the price's billing scheme.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a meter: request
curl https://api.payments.lk/v1/meters \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: meter-sms" \
  -d '{
    "name": "SMS sent",
    "eventName": "sms.sent",
    "aggregation": "sum"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "meter",
  "id": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "mode": "test",
  "name": "SMS sent",
  "eventName": "sms.sent",
  "aggregation": "sum",
  "active": true,
  "createdAt": "2026-10-01T04:00:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
namestringrequiredIts name, 1 to 80 characters, as invoice lines show the usage.1 to 80 characters
eventNamestringrequiredThe name your meter events will carry: lower case letters, digits, dots, dashes and underscores, up to 100, unique in the mode.
aggregationone of: sum, count, lastsum (the default), count or last.default "sum"

Returns 201 with a Meter object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running. Another meter in this mode already counts that eventName.idempotency_key_reusedidempotency_key_in_progressmeter_exists
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List meters

get/v1/meters

Your meters in this key's mode, newest first.

Key
A secret key (permission billing:read)
List meters: request
curl https://api.payments.lk/v1/meters \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "meter",
      "id": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
      "mode": "test",
      "name": "SMS sent",
      "eventName": "sms.sent",
      "aggregation": "sum",
      "active": true,
      "createdAt": "2026-10-01T04:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.

Returns 200 with a list of Meter objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Retrieve a meter

get/v1/meters/{id}

One meter.

Key
A secret key (permission billing:read)
Retrieve a meter: request
curl https://api.payments.lk/v1/meters/mtr_a4c8v2x9q1m7k3r6t0yhwn5z \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "meter",
  "id": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "mode": "test",
  "name": "SMS sent",
  "eventName": "sms.sent",
  "aggregation": "sum",
  "active": true,
  "createdAt": "2026-10-01T04:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe meter's id, mtr_ followed by 24 characters.

Returns 200 with a Meter object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo meter with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Update a meter

post/v1/meters/{id}

Renames a meter, or turns it off (active false: its events are refused, and its prices bill what it counted) or back on.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Update a meter: request
curl https://api.payments.lk/v1/meters/mtr_a4c8v2x9q1m7k3r6t0yhwn5z \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: meter-sms-rename" \
  -d '{
    "name": "Messages sent"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "meter",
  "id": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "mode": "test",
  "name": "Messages sent",
  "eventName": "sms.sent",
  "aggregation": "sum",
  "active": true,
  "createdAt": "2026-10-01T04:00:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe meter's id, mtr_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
namestringIts new name.1 to 80 characters
activebooleanFalse turns it off: its events are refused. True turns it back on.

Returns 200 with a Meter object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo meter with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Summarise a customer's usage

get/v1/meters/{id}/event_summaries

A customer's usage on the meter between two times, as the meter adds it up: what a metered price would bill for that span.

Key
A secret key (permission billing:read)
Summarise a customer's usage: request
curl "https://api.payments.lk/v1/meters/mtr_a4c8v2x9q1m7k3r6t0yhwn5z/event_summaries?customerId=cus_q8w3n5v1x7c2m9r4t6ya0kzp&start=2026-10-01T04%3A12%3A31.000Z&end=2026-11-01T04%3A12%3A31.000Z" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "meter_event_summary",
  "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "start": "2026-10-01T04:12:31.000Z",
  "end": "2026-11-01T04:12:31.000Z",
  "aggregatedValue": 12480
}

Path parameters

NameDescription
idstringrequiredThe meter's id, mtr_ followed by 24 characters.

Query parameters

NameDescription
customerIdstringrequiredThe customer.
startstring, ISO 8601 timerequiredFrom this time, ISO 8601.
endstring, ISO 8601 timerequiredUp to (not including) this time, ISO 8601, after start.

Returns 200 with a MeterEventSummary object.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo meter with this id for this key.
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Report usage

post/v1/meter_events

One use of a customer's, counted by the active meter whose eventName it carries, dated when it happened (within the last 35 days). Send your own identifier so a retry never counts twice: the same identifier answers with the event already kept. Usage alerts the customer's usage now reaches fire as billing.alert.triggered.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Report usage: request
curl https://api.payments.lk/v1/meter_events \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: usage-msg-88412" \
  -d '{
    "eventName": "sms.sent",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "value": 3,
    "timestamp": "2026-10-18T06:40:12.000Z",
    "identifier": "msg-88412"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "meter_event",
  "id": "mev_e2x7v1c9q4m8k3r6t0ynhw5p",
  "mode": "test",
  "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "eventName": "sms.sent",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "value": 3,
  "timestamp": "2026-10-18T06:40:12.000Z",
  "identifier": "msg-88412",
  "createdAt": "2026-10-18T06:40:12.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
eventNamestringrequiredThe eventName of an active meter of yours in this mode.
customerIdstringrequiredThe customer whose usage it is.
valueintegerHow much, a whole number from 0. Defaults to 1.0 to 1000000000; default 1
timestampstring, ISO 8601 timeWhen it happened, ISO 8601: within the last 35 days, and not ahead of now. Defaults to now.
identifierstringYour own id for it, up to 100 characters: sending it again returns the event already kept.at most 100 characters

Returns 201 with a MeterEvent object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. No active meter counts that eventName, or the timestamp is out of range.json_requiredinvalid_jsonidempotency_key_missingmeter_invalidbilling_date_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Create a usage alert

post/v1/usage_alerts

Sends billing.alert.triggered once a customer's usage on the meter in their current period reaches the threshold, once per customer: for one customer, or for every customer.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Create a usage alert: request
curl https://api.payments.lk/v1/usage_alerts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: alert-sms-10k" \
  -d '{
    "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
    "title": "10,000 messages",
    "threshold": 10000
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "usage_alert",
  "id": "ual_t3v9x1c7q2m8k4r6y0wnhz5p",
  "mode": "test",
  "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "customerId": null,
  "title": "10,000 messages",
  "threshold": 10000,
  "active": true,
  "createdAt": "2026-10-01T04:05:00.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
meterIdstringrequiredThe meter to watch.
customerIdstringOne customer to watch. Leave it out to watch every customer.
titlestringrequiredIts name, 1 to 80 characters, sent with the event.1 to 80 characters
thresholdintegerrequiredThe usage in a customer's current period that fires it, from 1.1 to 1000000000

Returns 201 with an UsageAlert object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots. The meter is not one of yours in this mode.json_requiredinvalid_jsonidempotency_key_missingmeter_invalid
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

List usage alerts

get/v1/usage_alerts

Your usage alerts in this key's mode, newest first. Filter by meter.

Key
A secret key (permission billing:read)
List usage alerts: request
curl "https://api.payments.lk/v1/usage_alerts?meterId=mtr_a4c8v2x9q1m7k3r6t0yhwn5z" \
  -H "Authorization: Bearer sk_test_..."

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "list",
  "data": [
    {
      "object": "usage_alert",
      "id": "ual_t3v9x1c7q2m8k4r6y0wnhz5p",
      "mode": "test",
      "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
      "customerId": null,
      "title": "10,000 messages",
      "threshold": 10000,
      "active": true,
      "createdAt": "2026-10-01T04:05:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Query parameters

NameDescription
limitintegerHow many to return, 1 to 100. Defaults to 25.1 to 100; default 25
cursorstringThe nextCursor of the page before. Leave it out for the first page.
meterIdstringOnly the alerts watching this meter.

Returns 200 with a list of UsageAlert objects.

Errors

StatusWhen
400VALIDATION_FAILEDA query parameter is not valid; fields names each one.
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Archive a usage alert

post/v1/usage_alerts/{id}/archive

Stops the alert firing for anyone new. Those it fired for stay recorded.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Archive a usage alert: request
curl -X POST https://api.payments.lk/v1/usage_alerts/ual_t3v9x1c7q2m8k4r6y0wnhz5p/archive \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: alert-sms-10k-off"

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 200
{
  "object": "usage_alert",
  "id": "ual_t3v9x1c7q2m8k4r6y0wnhz5p",
  "mode": "test",
  "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
  "customerId": null,
  "title": "10,000 messages",
  "threshold": 10000,
  "active": false,
  "createdAt": "2026-10-01T04:05:00.000Z"
}

Path parameters

NameDescription
idstringrequiredThe usage alert's id, ual_ followed by 24 characters.

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Returns 200 with an UsageAlert object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo usage alert with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Open the customer portal

post/v1/billing_portal/sessions

A customer's way into the customer portal, where they see their subscriptions and invoices, change their card, and, as your billing settings allow, switch plan or cancel. Send the customer to url straight away: it works for an hour. Make one each time a signed in customer asks to manage their subscription. A new card is taken by a Rs. 10 check that is refunded at once.

Key
A secret key (permission billing:write)
Idempotency-Key
Required
Open the customer portal: request
curl https://api.payments.lk/v1/billing_portal/sessions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: portal-member-1042-20261016" \
  -d '{
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "returnUrl": "https://gym.example/account"
  }'

The Node and PHP libraries do not have subscription methods yet. Call it over HTTP with your secret key, as shown; the libraries' types already describe the objects and the events.

Response 201
{
  "object": "billing_portal.session",
  "id": "bps_k3v8x1c6q9m2r5t7y0wn4hzp",
  "mode": "test",
  "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
  "url": "https://payments.lk/billing/Xq7d2V9kC4mZt1Wn8Rb5Hy3Lp6Gs0Jf2Ae4Uo7Ik9Nx",
  "returnUrl": "https://gym.example/account",
  "expiresAt": "2026-10-16T05:12:31.000Z",
  "createdAt": "2026-10-16T04:12:31.000Z"
}

Headers

NameDescription
Idempotency-KeystringrequiredMakes the write safe to retry. A key is remembered for 24 hours, separately for test and live keys: within that time the same key with the same body gets the first answer back instead of making a second one, and after it the key counts as new. Use a new key for every new write. 8 to 255 letters, digits, dashes, underscores, colons or dots.

Body

FieldDescription
customerIdstringrequiredThe customer, one of yours in this key's mode.
returnUrlstringWhere the portal's back link takes the customer: https, or your app's scheme.at most 2000 characters

Returns 201 with a BillingPortalSession object.

Errors

StatusWhen
400VALIDATION_FAILEDA field is missing or not valid, fields names each one; or the body is not JSON. The Idempotency-Key header is missing, or is not 8 to 255 letters, digits, dashes, underscores, colons or dots.json_requiredinvalid_jsonidempotency_key_missing
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
404NOT_FOUNDNo customer with this id for this key.
409CONFLICTThe Idempotency-Key was already used with a different request, or the first request with it is still running.idempotency_key_reusedidempotency_key_in_progress
413VALIDATION_FAILEDThe body is larger than the API reads.body_too_large
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Account

The merchant account behind the key.

Retrieve your account

get/v1/account

The merchant behind the key: status, plan, this month's usage against the plan's monthly limit in the key's mode, and whether live payments are open. A payment that would take the month past the limit is refused with monthly_limit_reached; usage says how close the month is.

Key
A secret key (permission account:read)
curl https://api.payments.lk/v1/account \
  -H "Authorization: Bearer sk_test_..."

Response 200
{
  "id": "mer_yb1xzmzqdza69g0p3hpeaew6",
  "tradingName": "Colombo Kottu House",
  "status": "activated",
  "applicationStatus": "approved",
  "clearance": {
    "tier": "starter",
    "monthlyCapCents": 10000000
  },
  "pricingPlan": "standard",
  "plan": "starter",
  "usage": {
    "month": "2026-09",
    "mode": "test",
    "plan": "starter",
    "capCents": 10000000,
    "settledCents": 6340000,
    "reservedCents": 0,
    "percent": 63,
    "resetsAt": "2026-09-30T18:30:00.000Z",
    "refusedCount": 0,
    "refusedCents": 0
  },
  "locale": "en",
  "liveEnabled": true,
  "phase1Position": null
}

Returns 200 with an Account object.

Errors

StatusWhen
401UNAUTHENTICATEDThe key is missing, malformed, or not a live key: revoked, expired or never issued.missing_api_keymalformed_api_keyunknown_api_key
403FORBIDDENThis kind of key cannot do this: a publishable key may only create checkouts, and an agent key opens no REST endpoint.key_not_permitted
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Reference

This document.

This document

get/v1/openapi.json

The OpenAPI 3.1 description of this API, generated from the same schemas the API validates with. No key needed; any origin may read it.

Key
None. This document is public.
This document: request
curl https://api.payments.lk/v1/openapi.json

No key and no library needed: any HTTP client, or an OpenAPI tool pointed at the URL.

Response 200
{
  "openapi": "3.1.0",
  "info": {
    "title": "Payments.lk API",
    "version": "v1"
  }
}

Returns 200 with this OpenAPI document.

Errors

StatusWhen
429RATE_LIMITEDMore than 120 requests a minute from one address to this endpoint. Wait for Retry-After seconds.
500INTERNALSomething failed on our side. Retry, with the same Idempotency-Key for a write.

Objects

What the endpoints and webhooks return, field by field.

Checkout

A payment and the hosted page that takes it.

Fields

FieldDescription
object"checkout"requiredAlways "checkout".
idstringrequiredThe checkout's id, chk_ followed by 24 characters.
modeone of: test, liverequiredtest for a sandbox key, live for a live key. The key decides it; a key never reads the other mode's rows.
statusone of: open, processing, completed, expired, canceledrequiredopen until the customer pays or it expires; processing while the processor decides; then completed, expired or canceled. An open checkout past expiresAt reads as expired.
urlstringrequiredThe hosted checkout page, in the checkout's language. Send the customer here.
expiresAtstring, ISO 8601 timerequiredWhen the checkout stops taking a payment, 30 minutes after it was made.
paymentPaymentrequiredThe payment the checkout takes.

Payment

One payment, however it was made: a checkout, a payment link, a checkout page or a saved card.

Fields

FieldDescription
object"payment"requiredAlways "payment".
idstringrequiredThe payment's id, pay_ followed by 24 characters.
modeone of: test, liverequiredtest for a sandbox key, live for a live key. The key decides it; a key never reads the other mode's rows.
statusone of: requires_payment_method, processing, succeeded, failed, canceled, partially_refunded, refunded, disputedrequiredrequires_payment_method until the customer pays; processing while the processor decides; then succeeded or failed. canceled when its checkout expired unpaid. partially_refunded and refunded once refunds succeed. disputed is reserved for chargebacks, which are not reported yet.
amountCentsintegerrequiredThe amount in LKR cents. Rs. 3,500 is 350000.
currency"LKR"requiredAlways "LKR".
descriptionstringrequiredWhat the customer is paying for, as you sent it.
referencestring or nullrequiredYour own reference, such as an order number, or null.
customerobject or nullrequiredThe customer's details, sent with the checkout or typed on the checkout page. Null when there are none.
customer.namestring or nullrequiredThe customer's name, or null.
customer.emailstring or nullrequiredThe customer's email address, lower case, or null.
customer.phonestring or nullrequiredThe customer's phone number, or null.
cardobject or nullrequiredThe card's scheme and last four digits, once the processor reports them. Null before that.
card.schemestring or nullrequiredThe card scheme as the processor names it, such as VISA or MASTERCARD, or null.
card.last4string or nullrequiredThe last four digits of the card number, or null.
cardSaveone of: not_requested, requested, saved, failedrequiredWhether the card was kept on file. not_requested: the checkout did not offer to keep it, or the customer did not agree. requested: keeping it was offered or agreed and the payment has not succeeded, so nothing is kept (and nothing will be, if it fails). saved: the card was kept, and card.saved names it. failed: the payment succeeded but the card was not kept, because the processor did not tokenise it or its answer carried no token; card.save_failed was sent, and the customer must add the card again.
feeCentsinteger or nullrequiredThe Payments.lk fee in cents, set when the payment succeeds. Null until then.
netCentsinteger or nullrequiredamountCents less feeCents, set when the payment succeeds. Null until then.
refundedCentsintegerrequiredHow much has been refunded so far, in cents.
failureMessagestring or nullrequiredWhy the payment failed, in a sentence you can show the customer. Null otherwise.
paymentLinkIdstring or nullrequiredThe payment link it was paid through, or null.
savedCardIdstring or nullrequiredThe saved card it was charged to, when it was charged without the customer present. Null otherwise.
invoiceIdstring or nullrequiredThe invoice this payment pays: a subscription's first payment, a renewal charged to the card on file, or a payment through a pay link. Null otherwise.
checkoutIdstring or nullrequiredThe checkout it belongs to, or null. A saved card charge has none.
checkoutPageIdstring or nullrequiredThe checkout page it was made on, or null.
orderobject or nullrequiredWhat the customer ordered, on a checkout page or through a checkout's lineItems, priced by us. Null for every other payment.
order.linesarray of objectrequiredThe lines of the order.
order.lines[].idstringrequiredThe line's id: the product's id on a checkout page, or line-1 to line-10 for lineItems in the order sent.
order.lines[].namestringrequiredThe product's name.
order.lines[].descriptionstringThe line's description, when it has one.
order.lines[].quantityintegerrequiredHow many the customer ordered.
order.lines[].unitAmountCentsintegerrequiredThe price of one, in cents.
order.lines[].amountCentsintegerrequiredThe line's total, in cents.
order.lines[].suggestedbooleanrequiredTrue when this is the product the page suggested and the customer added.
order.subtotalCentsintegerrequiredThe lines' total before discount, delivery and added tax, in cents.
order.discountobject or nullrequiredThe promotion code used and what it took off, or null.
order.discount.codestringrequiredThe promotion code.
order.discount.centsintegerrequiredWhat it took off, in cents.
order.shippingobject or nullrequiredThe delivery option chosen, or null.
order.shipping.idstringrequiredThe delivery option's id on the checkout page.
order.shipping.labelstringrequiredThe delivery option's name.
order.shipping.centsintegerrequiredWhat delivery cost, in cents.
order.taxobject or nullrequiredThe tax on the order, or null when the page charges none.
order.tax.modeone of: included, addedrequiredincluded when prices already hold the tax, added when it was added on top.
order.tax.rateBpsintegerrequiredThe tax rate in basis points: 1800 is 18%.
order.tax.centsintegerrequiredThe tax, in cents.
order.totalCentsintegerrequiredWhat the order came to, in cents.
deliveryobject or nullrequiredWhere to deliver, from a checkout page that asks. Null otherwise.
delivery.sameAsBillingbooleanrequiredTrue when the customer said to deliver to their billing address.
delivery.namestring or nullrequiredWho to deliver to, or null.
delivery.streetstring or nullrequiredThe street address, or null.
delivery.citystring or nullrequiredThe town or city, or null.
delivery.postcodestring or nullrequiredThe postal code, or null.
customFieldsarray of object or nullrequiredAnswers to the questions a checkout page asked, or null.
customFields[].keystringrequiredThe question's key on the checkout page.
customFields[].labelstringrequiredThe question as the customer saw it.
customFields[].valuestringrequiredThe customer's answer.
customerTaxIdstring or nullrequiredThe customer's tax number, when a checkout page asked for it. Null otherwise.
termsAcceptedAtstring, ISO 8601 time or nullrequiredWhen the customer accepted the checkout page's terms, or null.
createdAtstring, ISO 8601 timerequiredWhen it was created, ISO 8601 in UTC.
succeededAtstring, ISO 8601 time or nullrequiredWhen the payment succeeded, or null.

Refund

Money going back to the customer's card, in full or in part.

Fields

FieldDescription
object"refund"requiredAlways "refund".
idstringrequiredThe refund's id, re_ followed by 24 characters.
modeone of: test, liverequiredtest for a sandbox key, live for a live key. The key decides it; a key never reads the other mode's rows.
paymentIdstringrequiredThe payment it refunds.
statusone of: pending, processing, succeeded, failedrequiredpending when recorded, processing once sent to the processor, then succeeded or failed. The refund.succeeded or refund.failed webhook tells you which.
amountCentsintegerrequiredThe amount refunded, in cents.
currency"LKR"requiredAlways "LKR".
reasonstring or nullrequiredThe reason you gave, or null.
failureMessagestring or nullrequiredWhy the refund failed, or null.
createdAtstring, ISO 8601 timerequiredWhen it was created, ISO 8601 in UTC.
succeededAtstring, ISO 8601 time or nullrequiredWhen the refund succeeded, or null.

Saved card

A card a customer chose to keep on file at a checkout.

Fields

FieldDescription
object"saved_card"requiredAlways "saved_card".
idstringrequiredThe saved card's id, card_ followed by 24 characters. It is not a card number and works only with your key.
modeone of: test, liverequiredtest for a sandbox key, live for a live key. The key decides it; a key never reads the other mode's rows.
customerEmailstring or nullrequiredThe email address of the customer who kept the card, or null.
schemestring or nullrequiredThe card scheme, such as VISA, or null.
last4string or nullrequiredThe last four digits of the card number, or null.
expiryobject or nullrequiredThe month the card expires, as the processor reported it, or null. A charge after it will be declined, so ask the customer for a new card before then.
expiry.monthintegerrequiredThe month, 1 to 12.1 to 12
expiry.yearintegerrequiredThe four digit year.
paymentIdstring or nullrequiredThe payment the customer made when they kept the card, or null for a card saved before this was recorded.
consentobject or nullrequiredWhat the customer agreed to when they kept the card, or null for a card saved before this was recorded. Keep it: it is your record of why you may charge them.
consent.agreedAtstring, ISO 8601 timerequiredWhen the customer agreed, ISO 8601.
consent.localeone of: en, si, tarequiredThe language the customer was reading: en, si or ta.
consent.wordingstringrequiredWhich wording the customer read, such as checkoutPage.saveCard@1. The sentence itself is on our checkout page in that language, and a change to the words mints a new id rather than rewriting this one.
activebooleanrequiredFalse once the card has been deleted.
createdAtstring, ISO 8601 timerequiredWhen it was created, ISO 8601 in UTC.
retiredAtstring, ISO 8601 time or nullrequiredWhen the card was deleted, ISO 8601, or null while it is still usable.

Account

The merchant account behind the key.

Fields

FieldDescription
idstringrequiredYour merchant id, mer_ followed by 24 characters.
tradingNamestring or nullrequiredThe name customers see, or null before you have given one.
statusone of: prospect, applying, submitted, in_review, needs_info, approved, queued, activated, rejected, suspended, closedrequiredWhere your account is: prospect and applying while you fill in the application, submitted, in_review and needs_info while it is reviewed, approved, queued and activated after; rejected, suspended or closed when it cannot take payments. Live payments need activated.
applicationStatusone of: draft, submitted, in_review, needs_info, approved, rejected or nullrequiredYour application's status, or null before you have started one.
clearanceobjectrequiredKept for integrations written before plans. Read plan and usage instead.
clearance.tierone of: starter, verified, enterpriserequiredstarter for the Starter plan, verified for Growth, enterprise for Enterprise.
clearance.monthlyCapCentsinteger or nullrequiredYour plan's monthly limit in cents, or null for no limit. The same figure as usage.capCents.
pricingPlanstringrequiredThe pricing plan your fees are worked out on.
planone of: starter, growth, enterpriserequiredstarter, growth or enterprise. Starter and Growth take card payments up to a monthly limit, the ceiling of their pricing bracket; Enterprise has none. Upgrade from the dashboard's Plans page.
usageobjectrequiredThis month's card payments in the key's mode against your plan's monthly limit. Test mode counts separately, against the same limit.
usage.monthstringrequiredThe calendar month in Sri Lanka, such as 2026-09.
usage.modeone of: live, testrequiredlive or test: the mode of the key that asked.
usage.planone of: starter, growth, enterpriserequiredThe plan this month is counted against. An upgrade changes it at once.
usage.capCentsinteger or nullrequiredThe monthly limit in cents, or null for no limit.
usage.settledCentsintegerrequiredPayments that succeeded this month, in cents, gross: refunds do not free up room.
usage.reservedCentsintegerrequiredPayments with the processor now, in cents. They count toward the limit until they settle or fail.
usage.percentintegerrequiredWhole percent of the limit used by settled payments, rounded down. 0 with no limit.
usage.resetsAtstringrequiredWhen the limit resets: midnight on the 1st in Sri Lanka, as ISO 8601 in UTC.
usage.refusedCountintegerrequiredPayments refused this month because they would have passed the limit.
usage.refusedCentsintegerrequiredWhat those refused payments came to, in cents.
localeone of: en, si, tarequiredThe language of your dashboard and emails: en, si or ta.
liveEnabledbooleanrequiredTrue once live keys can take payments.
phase1Positioninteger or nullrequiredYour place in the launch queue if you were approved beyond the launch cap, or null.

Event

Something that happened on your account, in the same shape the webhook delivers it.

Fields

FieldDescription
idstringrequiredThe event's id, evt_ followed by 24 characters. Deliveries of the same event carry the same id, so use it to ignore a repeat.
object"event"requiredAlways "event".
typeone of: payment.succeeded, payment.failed, checkout.expired, card.saved, card.save_failed, card.expiring, billing.alert.triggered, subscription_schedule.created, subscription_schedule.completed, subscription_schedule.released, subscription_schedule.canceled, refund.succeeded, refund.failed, account.limit.approaching, account.limit.reached, account.limit.reset, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.trial_will_end, customer.subscription.paused, customer.subscription.resumed, invoice.created, invoice.finalized, invoice.sent, invoice.overdue, credit_note.created, entitlements.active_entitlement_summary.updated, invoice.paid, invoice.payment_failed, invoice.upcoming, invoice.voided, invoice.marked_uncollectiblerequiredWhat happened.
modeone of: test, liverequiredtest for a sandbox key, live for a live key. The key decides it; a key never reads the other mode's rows.
createdstring, ISO 8601 timerequiredWhen the event happened, ISO 8601 in UTC.
dataobjectrequiredThe object the event is about. Its shape depends on type; see each event under Webhooks.

Refund with its payment

Fields

FieldDescription
object"refund"requiredAlways "refund".
idstringrequiredThe refund's id, re_ followed by 24 characters.
modeone of: test, liverequiredtest for a sandbox key, live for a live key. The key decides it; a key never reads the other mode's rows.
paymentIdstringrequiredThe payment it refunds.
statusone of: pending, processing, succeeded, failedrequiredpending when recorded, processing once sent to the processor, then succeeded or failed. The refund.succeeded or refund.failed webhook tells you which.
amountCentsintegerrequiredThe amount refunded, in cents.
currency"LKR"requiredAlways "LKR".
reasonstring or nullrequiredThe reason you gave, or null.
failureMessagestring or nullrequiredWhy the refund failed, or null.
createdAtstring, ISO 8601 timerequiredWhen it was created, ISO 8601 in UTC.
succeededAtstring, ISO 8601 time or nullrequiredWhen the refund succeeded, or null.
paymentPaymentrequiredThe payment the refund is for, as it stands after the refund.

Expired checkout

Fields

FieldDescription
object"checkout"requiredAlways "checkout".
idstringrequiredThe checkout's id.
paymentIdstringrequiredThe id of the payment it would have taken, now canceled.

Error

Every error, whatever the status.

Fields

FieldDescription
codeone of: VALIDATION_FAILED, UNAUTHENTICATED, FORBIDDEN, NOT_FOUND, CONFLICT, RATE_LIMITED, NOT_SUPPORTED, SERVICE_UNAVAILABLE, INTERNALrequiredA stable code for the kind of error. Match on this and on reason, never on the message.
reasonstringThe exact cause in snake_case, when there is a more precise one than the code, such as merchant_not_live.
messagestringrequiredA sentence you can show a person. It never carries internal detail.
docUrlstring, URLrequiredWhere this error is explained, on https://payments.lk/developers/errors, anchored at its reason or its code.
traceIdstringrequiredQuote this when you contact support; it finds the request in our logs.
fieldsarray of objectFor VALIDATION_FAILED: each field that was refused and why.
fields[].pathstringrequiredThe field, as a dotted path such as customer.email.
fields[].messagestringrequiredWhy it was refused.

Webhooks

Add an endpoint in your dashboard under Developers: its URL, test or live, and the events it should receive. Its signing secret, starting whsec_, is shown once. There is no API for managing endpoints yet. Each event is sent as a POST of JSON, one event per request, to every active endpoint of the same mode that takes its type. On an endpoint’s page you can change its URL and events, turn it off, rotate its signing secret, and send it a test.ping event, which carries no payment and goes to that endpoint alone; each delivery shows the exact body sent and every attempt, and can be sent again.

Delivery body
{
  "id": "evt_...",
  "object": "event",
  "type": "payment.succeeded",
  "mode": "test",
  "created": "2026-09-18T09:16:41.000Z",
  "data": { ... }
}

HeaderValue
Content-Typeapplication/json
User-AgentPayments.lk-Webhooks/1.0
Payments-Signaturet=<unix seconds>,v1=<signature>, with a second v1 while a rotated secret is kept. See Verifying signatures.
Payments-Event-IdThe event's id, the same as id in the body.

Answer with any 2xx within 10 seconds and the delivery is done. Anything else, or no answer, is tried again after 1 minute, then 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours after each further failure; when the last try fails too, the delivery is marked failed. In production an endpoint must be an https URL on port 443 at a public address, an account holds up to ten endpoints in each mode, and redirects are not followed. A delivery can repeat and events can arrive out of order, so keep the event id and ignore one you have handled. To catch up on anything missed, read GET /v1/events, which lists the same events in the same shape.

Verifying signatures

The Payments-Signature header holds t, the time of signing in unix seconds, and v1, the hex HMAC-SHA256 of the text t + "." + body under the endpoint’s signing secret, where body is the raw request body exactly as received. To verify: compute the same HMAC, compare it with v1 in constant time, and refuse the delivery if t is more than 300 seconds from your clock. Verify before you parse the JSON; a parsed and re-serialised body will not match. After you rotate a signing secret and keep the old one for a while, the header carries a v1 for each secret until then, the new one last: accept the delivery if any of them matches, as the code below does.

import express from "express";
import { PaymentsLk } from "@payments-lk/node";

const lk = new PaymentsLk(process.env.PAYMENTS_LK_SECRET_KEY);
const app = express();

// express.raw keeps the exact bytes: the signature is over them, not over parsed JSON.
app.post("/webhooks/payments-lk", express.raw({ type: "application/json" }), async (req, res) => {
  let event;
  try {
    event = lk.webhooks.constructEvent(req.body, req.headers["payments-signature"], process.env.PAYMENTS_LK_WEBHOOK_SECRET);
  } catch {
    return res.sendStatus(400); // not signed with your secret, or too old
  }
  if (await events.seen(event.id)) return res.sendStatus(200); // a delivery can repeat
  if (event.type === "payment.succeeded") await orders.markPaid(event.data.reference, event.data.amountCents);
  res.sendStatus(200);
});

Events

Each event’s data is an object described under Objects. Every example is checked in our tests against the code that sends it.

payment.succeeded

The card was authorised and the payment succeeded. data is the payment, with its fee. Fulfil the order from this event.

payment.succeeded example
{
  "id": "evt_ev85ebwj9yqjq81vwf5qwz9m",
  "object": "event",
  "type": "payment.succeeded",
  "mode": "test",
  "created": "2026-09-18T09:16:41.000Z",
  "data": {
    "object": "payment",
    "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "mode": "test",
    "status": "succeeded",
    "amountCents": 350000,
    "currency": "LKR",
    "description": "Two kottu and a milk tea",
    "reference": "order-8891",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "card": {
      "scheme": "VISA",
      "last4": "4242"
    },
    "cardSave": "not_requested",
    "feeCents": 8015,
    "netCents": 341985,
    "refundedCents": 0,
    "failureMessage": null,
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
    "checkoutPageId": null,
    "order": null,
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-18T09:14:05.000Z",
    "succeededAt": "2026-09-18T09:16:41.000Z"
  }
}

payment.failed

The card was declined or the payment was not completed. data is the payment, with failureMessage.

payment.failed example
{
  "id": "evt_wxb2ycr0s580brjpes23aw67",
  "object": "event",
  "type": "payment.failed",
  "mode": "test",
  "created": "2026-09-17T18:42:30.000Z",
  "data": {
    "object": "payment",
    "id": "pay_hj31hwy2mavtcbrfb2b2zz3n",
    "mode": "test",
    "status": "failed",
    "amountCents": 520000,
    "currency": "LKR",
    "description": "Lamprais for four",
    "reference": "order-8874",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "card": {
      "scheme": "MASTERCARD",
      "last4": "5100"
    },
    "cardSave": "not_requested",
    "feeCents": null,
    "netCents": null,
    "refundedCents": 0,
    "failureMessage": "The card was declined.",
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_4ne3cpz1r2a8g5vq7xk0m9dt",
    "checkoutPageId": null,
    "order": null,
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-17T18:40:12.000Z",
    "succeededAt": null
  }
}

checkout.expired

Nobody paid the checkout before it expired. data names the checkout and its payment, which is now canceled.

checkout.expired example
{
  "id": "evt_r0my8wv91rgsx5fjb848aqpp",
  "object": "event",
  "type": "checkout.expired",
  "mode": "test",
  "created": "2026-09-18T10:31:00.000Z",
  "data": {
    "object": "checkout",
    "id": "chk_wnqa1fax67k19rt7td9cc3j2",
    "paymentId": "pay_xsrps207y12k0c1y5f85jbvz"
  }
}

card.saved

The customer kept their card on file. data is the saved card; keep its id to charge it later.

card.saved example
{
  "id": "evt_w2fx9rtd0sa7fx2kt2vhbh9a",
  "object": "event",
  "type": "card.saved",
  "mode": "test",
  "created": "2026-09-02T07:30:44.000Z",
  "data": {
    "object": "saved_card",
    "id": "card_g2a1rex14ff7qh2dc6j6qzmq",
    "mode": "test",
    "customerEmail": "[email protected]",
    "scheme": "VISA",
    "last4": "4242",
    "expiry": {
      "month": 11,
      "year": 2029
    },
    "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "consent": {
      "agreedAt": "2026-09-02T07:30:41.000Z",
      "locale": "en",
      "wording": "checkoutPage.saveCard@1"
    },
    "active": true,
    "createdAt": "2026-09-02T07:30:44.000Z",
    "retiredAt": null
  }
}

card.save_failed

A payment that was to keep the customer's card succeeded, but the card was not kept: Payable did not tokenise it, or its answer carried no token. data is the payment, whose cardSave is failed; no card.saved follows. Ask the customer to add the card again. A subscription it was for, left with no card, shows cardMissingSince, and its renewals are emailed to the customer to pay, with a link to add a card.

card.save_failed example
{
  "id": "evt_c8f3v1x6q9m2k5r7t0ynhw4z",
  "object": "event",
  "type": "card.save_failed",
  "mode": "test",
  "created": "2026-09-18T09:16:41.000Z",
  "data": {
    "object": "payment",
    "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "mode": "test",
    "status": "succeeded",
    "amountCents": 350000,
    "currency": "LKR",
    "description": "Two kottu and a milk tea",
    "reference": "order-8891",
    "customer": {
      "name": "Nimali Perera",
      "email": "[email protected]",
      "phone": "0771234567"
    },
    "card": {
      "scheme": "VISA",
      "last4": "4242"
    },
    "cardSave": "failed",
    "feeCents": 8015,
    "netCents": 341985,
    "refundedCents": 0,
    "failureMessage": null,
    "paymentLinkId": null,
    "savedCardId": null,
    "invoiceId": null,
    "checkoutId": "chk_z6zf7gkx4fcwy3402kmwrm3w",
    "checkoutPageId": null,
    "order": null,
    "delivery": null,
    "customFields": null,
    "customerTaxId": null,
    "termsAcceptedAt": null,
    "createdAt": "2026-09-18T09:14:05.000Z",
    "succeededAt": "2026-09-18T09:16:41.000Z"
  }
}

card.expiring

A saved card that a running subscription is charged to expires at the end of this month. data is the saved card; the customer is emailed a link to the customer portal to change it.

card.expiring example
{
  "id": "evt_c7x2v9k1q4m8e3r6t0ynhz5w",
  "object": "event",
  "type": "card.expiring",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "saved_card",
    "id": "card_g2a1rex14ff7qh2dc6j6qzmq",
    "mode": "test",
    "customerEmail": "[email protected]",
    "scheme": "VISA",
    "last4": "4242",
    "expiry": {
      "month": 11,
      "year": 2029
    },
    "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "consent": {
      "agreedAt": "2026-09-02T07:30:41.000Z",
      "locale": "en",
      "wording": "checkoutPage.saveCard@1"
    },
    "active": true,
    "createdAt": "2026-09-02T07:30:44.000Z",
    "retiredAt": null
  }
}

billing.alert.triggered

A customer's usage on a meter in their current period reached a usage alert's threshold. data is the alert, the customer and the usage; it is sent once per alert and customer.

billing.alert.triggered example
{
  "id": "evt_b8a2v7x1c9q4m3k6r0tnhw5z",
  "object": "event",
  "type": "billing.alert.triggered",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "usage_alert_trigger",
    "alertId": "ual_t3v9x1c7q2m8k4r6y0wnhz5p",
    "title": "10,000 API calls",
    "meterId": "mtr_a4c8v2x9q1m7k3r6t0yhwn5z",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "threshold": 10000,
    "value": 10012,
    "triggeredAt": "2026-10-18T06:40:12.000Z"
  }
}

subscription_schedule.created

A subscription schedule was made. data is the schedule; its subscription starts on startDate.

subscription_schedule.created example
{
  "id": "evt_d1s8v3x7c2q9m4k6r0tnhw5y",
  "object": "event",
  "type": "subscription_schedule.created",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription_schedule",
    "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
    "mode": "test",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "status": "active",
    "endBehavior": "cancel",
    "startDate": "2026-10-01T04:12:31.000Z",
    "phases": [
      {
        "items": [
          {
            "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
            "quantity": 1
          }
        ],
        "iterations": 12,
        "trialDays": null,
        "couponId": null
      }
    ],
    "currentPhase": 0,
    "currentPhaseEndsAt": "2027-10-01T04:12:31.000Z",
    "reference": "Diploma, 12 installments",
    "completedAt": null,
    "releasedAt": null,
    "canceledAt": null,
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

subscription_schedule.completed

A schedule's last phase ended and, as its endBehavior is cancel, so did its subscription. data is the schedule.

subscription_schedule.completed example
{
  "id": "evt_d2s9v4x8c3q1m5k7r0ynhw6z",
  "object": "event",
  "type": "subscription_schedule.completed",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription_schedule",
    "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
    "mode": "test",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "status": "completed",
    "endBehavior": "cancel",
    "startDate": "2026-10-01T04:12:31.000Z",
    "phases": [
      {
        "items": [
          {
            "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
            "quantity": 1
          }
        ],
        "iterations": 12,
        "trialDays": null,
        "couponId": null
      }
    ],
    "currentPhase": 0,
    "currentPhaseEndsAt": null,
    "reference": "Diploma, 12 installments",
    "completedAt": "2027-10-01T04:12:31.000Z",
    "releasedAt": null,
    "canceledAt": null,
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

subscription_schedule.released

A schedule let go of its subscription, which carries on as it is: after its last phase with endBehavior release, or released by you. data is the schedule.

subscription_schedule.released example
{
  "id": "evt_d3s1v5x9c4q2m6k8r0hwnz7y",
  "object": "event",
  "type": "subscription_schedule.released",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription_schedule",
    "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
    "mode": "test",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "status": "released",
    "endBehavior": "release",
    "startDate": "2026-10-01T04:12:31.000Z",
    "phases": [
      {
        "items": [
          {
            "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
            "quantity": 1
          }
        ],
        "iterations": 12,
        "trialDays": null,
        "couponId": null
      }
    ],
    "currentPhase": 0,
    "currentPhaseEndsAt": null,
    "reference": "Diploma, 12 installments",
    "completedAt": null,
    "releasedAt": "2027-10-01T04:12:31.000Z",
    "canceledAt": null,
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

subscription_schedule.canceled

A schedule was canceled, by you or because its subscription ended some other way. data is the schedule.

subscription_schedule.canceled example
{
  "id": "evt_d4s2v6x1c5q3m7k9r0wnhy8z",
  "object": "event",
  "type": "subscription_schedule.canceled",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription_schedule",
    "id": "sched_c6v2x9q1m8k4r7t3y0hwnz5p",
    "mode": "test",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "status": "canceled",
    "endBehavior": "cancel",
    "startDate": "2026-10-01T04:12:31.000Z",
    "phases": [
      {
        "items": [
          {
            "priceId": "price_i7c3v9x2q8m1k6r4t0ynwh5z",
            "quantity": 1
          }
        ],
        "iterations": 12,
        "trialDays": null,
        "couponId": null
      }
    ],
    "currentPhase": 0,
    "currentPhaseEndsAt": null,
    "reference": "Diploma, 12 installments",
    "completedAt": null,
    "releasedAt": null,
    "canceledAt": "2027-02-01T04:12:31.000Z",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

refund.succeeded

The refund went through. data is the refund, with the payment as it stands after it.

refund.succeeded example
{
  "id": "evt_np6d7c5k65jryr0jdgmavejf",
  "object": "event",
  "type": "refund.succeeded",
  "mode": "test",
  "created": "2026-09-19T10:05:52.000Z",
  "data": {
    "object": "refund",
    "id": "re_sbca5jeftvf7acqsx5rvdzk3",
    "mode": "test",
    "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "status": "succeeded",
    "amountCents": 50000,
    "currency": "LKR",
    "reason": "The milk tea was not available",
    "failureMessage": null,
    "createdAt": "2026-09-19T10:02:17.000Z",
    "succeededAt": "2026-09-19T10:05:52.000Z",
    "payment": {
      "object": "payment",
      "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
      "mode": "test",
      "status": "partially_refunded",
      "amountCents": 350000,
      "currency": "LKR",
      "description": "Two kottu and a milk tea",
      "reference": "order-8891",
      "customer": {
        "name": "Nimali Perera",
        "email": "[email protected]",
        "phone": "0771234567"
      },
      "card": {
        "scheme": "VISA",
        "last4": "4242"
      },
      "cardSave": "not_requested",
      "feeCents": 8015,
      "netCents": 341985,
      "refundedCents": 50000,
      "failureMessage": null,
      "paymentLinkId": null,
      "savedCardId": null,
      "invoiceId": null,
      "checkoutId": null,
      "checkoutPageId": null,
      "order": null,
      "delivery": null,
      "customFields": null,
      "customerTaxId": null,
      "termsAcceptedAt": null,
      "createdAt": "2026-09-18T09:14:05.000Z",
      "succeededAt": "2026-09-18T09:16:41.000Z"
    }
  }
}

refund.failed

The refund could not be made. data is the refund, with failureMessage, and the payment it was for.

refund.failed example
{
  "id": "evt_v2z2cg8qe8w2mcqxbs7s7btd",
  "object": "event",
  "type": "refund.failed",
  "mode": "test",
  "created": "2026-09-19T10:05:52.000Z",
  "data": {
    "object": "refund",
    "id": "re_sbca5jeftvf7acqsx5rvdzk3",
    "mode": "test",
    "paymentId": "pay_x1n31wnp8nkbjkkhymtqd6zs",
    "status": "failed",
    "amountCents": 50000,
    "currency": "LKR",
    "reason": "The milk tea was not available",
    "failureMessage": "The refund could not be processed.",
    "createdAt": "2026-09-19T10:02:17.000Z",
    "succeededAt": null,
    "payment": {
      "object": "payment",
      "id": "pay_x1n31wnp8nkbjkkhymtqd6zs",
      "mode": "test",
      "status": "succeeded",
      "amountCents": 350000,
      "currency": "LKR",
      "description": "Two kottu and a milk tea",
      "reference": "order-8891",
      "customer": {
        "name": "Nimali Perera",
        "email": "[email protected]",
        "phone": "0771234567"
      },
      "card": {
        "scheme": "VISA",
        "last4": "4242"
      },
      "cardSave": "not_requested",
      "feeCents": 8015,
      "netCents": 341985,
      "refundedCents": 0,
      "failureMessage": null,
      "paymentLinkId": null,
      "savedCardId": null,
      "invoiceId": null,
      "checkoutId": null,
      "checkoutPageId": null,
      "order": null,
      "delivery": null,
      "customFields": null,
      "customerTaxId": null,
      "termsAcceptedAt": null,
      "createdAt": "2026-09-18T09:14:05.000Z",
      "succeededAt": "2026-09-18T09:16:41.000Z"
    }
  }
}

account.limit.approaching

This month's live card payments reached 60, 70, 80 or 90 percent of your plan's monthly limit; threshold says which. Sent once a month per threshold. data is the month's usage, with projectedAt when the pace says when the limit will be reached.

account.limit.approaching example
{
  "id": "evt_k7m2q9x4v1c8r3t6y0wz5hnd",
  "object": "event",
  "type": "account.limit.approaching",
  "mode": "live",
  "created": "2026-09-19T11:40:00.000Z",
  "data": {
    "object": "account_usage",
    "month": "2026-09",
    "plan": "starter",
    "capCents": 10000000,
    "settledCents": 8120000,
    "percent": 81,
    "threshold": 80,
    "refusedCount": 0,
    "refusedCents": 0,
    "resetsAt": "2026-09-30T18:30:00.000Z",
    "projectedAt": "2026-09-24T09:12:00.000Z"
  }
}

account.limit.reached

Your plan's monthly limit now stands in the way of live card payments: the month reached it, or a payment that would pass it was refused. Payments that would pass the limit are refused until you upgrade or the month resets on the 1st. data is the month's usage.

account.limit.reached example
{
  "id": "evt_p3v8n1x6c9m2q5r7t0yk4hwz",
  "object": "event",
  "type": "account.limit.reached",
  "mode": "live",
  "created": "2026-09-24T09:15:00.000Z",
  "data": {
    "object": "account_usage",
    "month": "2026-09",
    "plan": "starter",
    "capCents": 10000000,
    "settledCents": 9750000,
    "percent": 97,
    "threshold": 100,
    "refusedCount": 1,
    "refusedCents": 450000,
    "resetsAt": "2026-09-30T18:30:00.000Z",
    "projectedAt": null
  }
}

account.limit.reset

A new month began after one that reached the limit. data is the finished month's usage, with how many payments the limit refused and what they came to.

account.limit.reset example
{
  "id": "evt_z9c4m7q2x8v1n5r3t6yh0kwp",
  "object": "event",
  "type": "account.limit.reset",
  "mode": "live",
  "created": "2026-09-30T18:31:00.000Z",
  "data": {
    "object": "account_usage",
    "month": "2026-09",
    "plan": "starter",
    "capCents": 10000000,
    "settledCents": 9750000,
    "percent": 97,
    "threshold": 100,
    "refusedCount": 3,
    "refusedCents": 1280000,
    "resetsAt": "2026-09-30T18:30:00.000Z",
    "projectedAt": null
  }
}

customer.subscription.created

A subscription was made. It is incomplete until its first invoice is paid. data is the subscription.

customer.subscription.created example
{
  "id": "evt_s1c8v2x7q4m9k3r6t0yhwn5z",
  "object": "event",
  "type": "customer.subscription.created",
  "mode": "test",
  "created": "2026-10-01T04:12:31.000Z",
  "data": {
    "object": "subscription",
    "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "status": "incomplete",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "price": {
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "productName": "Gym membership",
      "unitAmountCents": 450000,
      "interval": "month",
      "intervalCount": 1
    },
    "quantity": 1,
    "items": [
      {
        "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
        "price": {
          "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
          "productName": "Gym membership",
          "unitAmountCents": 450000,
          "interval": "month",
          "intervalCount": 1
        },
        "quantity": 1
      }
    ],
    "currentPeriodStart": "2026-10-01T04:12:31.000Z",
    "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
    "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": null,
    "endedAt": null,
    "cancelReason": null,
    "cancellationDetails": {
      "reason": null,
      "feedback": null,
      "comment": null
    },
    "trialStart": null,
    "trialEnd": null,
    "trialEndBehavior": "create_invoice",
    "pauseCollection": null,
    "discount": null,
    "taxRateIds": [],
    "subscriptionLinkId": null,
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "pendingUpdate": null,
    "cardId": null,
    "cardMissingSince": null,
    "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "checkoutUrl": "https://payments.lk/checkout/chk_r8v2x6c1q9m4k7t3y0wnzh5p",
    "reference": "member-1042",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

customer.subscription.updated

A subscription changed: it turned active, past_due or unpaid, moved to its next period, was set to cancel at period end, or expired unpaid. data is the subscription as it stands.

customer.subscription.updated example
{
  "id": "evt_s2v9x3c8q1m6k4r7t0ynhw5z",
  "object": "event",
  "type": "customer.subscription.updated",
  "mode": "test",
  "created": "2026-10-01T04:15:02.000Z",
  "data": {
    "object": "subscription",
    "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "status": "active",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "price": {
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "productName": "Gym membership",
      "unitAmountCents": 450000,
      "interval": "month",
      "intervalCount": 1
    },
    "quantity": 1,
    "items": [
      {
        "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
        "price": {
          "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
          "productName": "Gym membership",
          "unitAmountCents": 450000,
          "interval": "month",
          "intervalCount": 1
        },
        "quantity": 1
      }
    ],
    "currentPeriodStart": "2026-10-01T04:12:31.000Z",
    "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
    "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": null,
    "endedAt": null,
    "cancelReason": null,
    "cancellationDetails": {
      "reason": null,
      "feedback": null,
      "comment": null
    },
    "trialStart": null,
    "trialEnd": null,
    "trialEndBehavior": "create_invoice",
    "pauseCollection": null,
    "discount": null,
    "taxRateIds": [],
    "subscriptionLinkId": null,
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "pendingUpdate": null,
    "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
    "cardMissingSince": null,
    "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "checkoutUrl": null,
    "reference": "member-1042",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

customer.subscription.deleted

A subscription ended: canceled now, at the end of its period, or for non-payment after the last retry and the grace days. data is the subscription. Revoke access.

customer.subscription.deleted example
{
  "id": "evt_s3x1v6c9q2m8k5r4t0zyhw7n",
  "object": "event",
  "type": "customer.subscription.deleted",
  "mode": "test",
  "created": "2026-10-20T08:00:00.000Z",
  "data": {
    "object": "subscription",
    "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "status": "canceled",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "price": {
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "productName": "Gym membership",
      "unitAmountCents": 450000,
      "interval": "month",
      "intervalCount": 1
    },
    "quantity": 1,
    "items": [
      {
        "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
        "price": {
          "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
          "productName": "Gym membership",
          "unitAmountCents": 450000,
          "interval": "month",
          "intervalCount": 1
        },
        "quantity": 1
      }
    ],
    "currentPeriodStart": "2026-10-01T04:12:31.000Z",
    "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
    "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": "2026-10-20T08:00:00.000Z",
    "endedAt": "2026-10-20T08:00:00.000Z",
    "cancelReason": "requested_by_merchant",
    "cancellationDetails": {
      "reason": "requested_by_merchant",
      "feedback": "too_expensive",
      "comment": "Moving to the branch closer to home."
    },
    "trialStart": null,
    "trialEnd": null,
    "trialEndBehavior": "create_invoice",
    "pauseCollection": null,
    "discount": null,
    "taxRateIds": [],
    "subscriptionLinkId": null,
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "pendingUpdate": null,
    "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
    "cardMissingSince": null,
    "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "checkoutUrl": null,
    "reference": "member-1042",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

customer.subscription.trial_will_end

A subscription's trial ends in three days. The customer has been emailed what happens next. data is the subscription.

customer.subscription.trial_will_end example
{
  "id": "evt_s4t8v1x2c9q3m7k5r0yhwn6z",
  "object": "event",
  "type": "customer.subscription.trial_will_end",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription",
    "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "status": "trialing",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "price": {
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "productName": "Gym membership",
      "unitAmountCents": 450000,
      "interval": "month",
      "intervalCount": 1
    },
    "quantity": 1,
    "items": [
      {
        "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
        "price": {
          "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
          "productName": "Gym membership",
          "unitAmountCents": 450000,
          "interval": "month",
          "intervalCount": 1
        },
        "quantity": 1
      }
    ],
    "currentPeriodStart": "2026-10-01T04:12:31.000Z",
    "currentPeriodEnd": "2026-10-15T04:12:31.000Z",
    "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": null,
    "endedAt": null,
    "cancelReason": null,
    "cancellationDetails": {
      "reason": null,
      "feedback": null,
      "comment": null
    },
    "trialStart": "2026-10-01T04:12:31.000Z",
    "trialEnd": "2026-10-15T04:12:31.000Z",
    "trialEndBehavior": "create_invoice",
    "pauseCollection": null,
    "discount": null,
    "taxRateIds": [],
    "subscriptionLinkId": null,
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "pendingUpdate": null,
    "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
    "cardMissingSince": null,
    "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "checkoutUrl": null,
    "reference": "member-1042",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

customer.subscription.paused

A subscription was paused because its trial ended without a card on file and you chose to pause such subscriptions. Nothing is billed until it is resumed. data is the subscription.

customer.subscription.paused example
{
  "id": "evt_s5p2v7x1c8q4m9k3r0zyhw6n",
  "object": "event",
  "type": "customer.subscription.paused",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription",
    "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "status": "paused",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "price": {
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "productName": "Gym membership",
      "unitAmountCents": 450000,
      "interval": "month",
      "intervalCount": 1
    },
    "quantity": 1,
    "items": [
      {
        "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
        "price": {
          "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
          "productName": "Gym membership",
          "unitAmountCents": 450000,
          "interval": "month",
          "intervalCount": 1
        },
        "quantity": 1
      }
    ],
    "currentPeriodStart": "2026-10-01T04:12:31.000Z",
    "currentPeriodEnd": "2026-10-15T04:12:31.000Z",
    "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": null,
    "endedAt": null,
    "cancelReason": null,
    "cancellationDetails": {
      "reason": null,
      "feedback": null,
      "comment": null
    },
    "trialStart": "2026-10-01T04:12:31.000Z",
    "trialEnd": "2026-10-15T04:12:31.000Z",
    "trialEndBehavior": "create_invoice",
    "pauseCollection": null,
    "discount": null,
    "taxRateIds": [],
    "subscriptionLinkId": null,
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "pendingUpdate": null,
    "cardId": null,
    "cardMissingSince": null,
    "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "checkoutUrl": null,
    "reference": "member-1042",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

customer.subscription.resumed

A paused subscription was resumed: a new period started and its invoice is charged to the card now on file. data is the subscription.

customer.subscription.resumed example
{
  "id": "evt_s6r1v8x3c2q7m4k9t0nwhz5y",
  "object": "event",
  "type": "customer.subscription.resumed",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "subscription",
    "id": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "status": "active",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "price": {
      "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
      "productName": "Gym membership",
      "unitAmountCents": 450000,
      "interval": "month",
      "intervalCount": 1
    },
    "quantity": 1,
    "items": [
      {
        "id": "si_t4c9v2x7q1m6k8r3y0wnhz5p",
        "price": {
          "id": "price_m3v8q1x6c9k2r5t7y0wn4hzp",
          "productName": "Gym membership",
          "unitAmountCents": 450000,
          "interval": "month",
          "intervalCount": 1
        },
        "quantity": 1
      }
    ],
    "currentPeriodStart": "2026-10-01T04:12:31.000Z",
    "currentPeriodEnd": "2026-11-01T04:12:31.000Z",
    "billingCycleAnchor": "2026-10-01T04:12:31.000Z",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": null,
    "endedAt": null,
    "cancelReason": null,
    "cancellationDetails": {
      "reason": null,
      "feedback": null,
      "comment": null
    },
    "trialStart": null,
    "trialEnd": null,
    "trialEndBehavior": "create_invoice",
    "pauseCollection": null,
    "discount": null,
    "taxRateIds": [],
    "subscriptionLinkId": null,
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "pendingUpdate": null,
    "cardId": "card_g2a1rex14ff7qh2dc6j6qzmq",
    "cardMissingSince": null,
    "latestInvoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "checkoutUrl": null,
    "reference": "member-1042",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

invoice.created

An invoice was made: a subscription's first, or a renewal's draft, which is finalised and charged after the draft window. data is the invoice.

invoice.created example
{
  "id": "evt_i1c9v3x8q2m7k4r6t0ywnh5z",
  "object": "event",
  "type": "invoice.created",
  "mode": "test",
  "created": "2026-11-01T04:13:00.000Z",
  "data": {
    "object": "invoice",
    "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
    "mode": "test",
    "number": null,
    "status": "draft",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_cycle",
    "periodStart": "2026-11-01T04:12:31.000Z",
    "periodEnd": "2026-12-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-11-01T04:12:31.000Z",
        "periodEnd": "2026-12-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 0,
    "nextAttemptAt": null,
    "lastFailure": null,
    "paidAt": null,
    "hostedInvoiceUrl": null,
    "createdAt": "2026-11-01T04:13:00.000Z"
  }
}

invoice.finalized

An invoice was numbered and opened for payment. data is the invoice.

invoice.finalized example
{
  "id": "evt_i2v8x1c7q3m9k6r4t0yzhw5n",
  "object": "event",
  "type": "invoice.finalized",
  "mode": "test",
  "created": "2026-11-01T05:13:00.000Z",
  "data": {
    "object": "invoice",
    "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
    "mode": "test",
    "number": "INV-000002",
    "status": "open",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_cycle",
    "periodStart": "2026-11-01T04:12:31.000Z",
    "periodEnd": "2026-12-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-11-01T04:12:31.000Z",
        "periodEnd": "2026-12-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 0,
    "nextAttemptAt": "2026-11-01T05:13:00.000Z",
    "lastFailure": null,
    "paidAt": null,
    "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
    "createdAt": "2026-11-01T04:13:00.000Z"
  }
}

invoice.sent

An invoice collected by sending it was emailed to the customer, to pay on its page by its due date. data is the invoice.

invoice.sent example
{
  "id": "evt_i8s3v1x7c2q9m4k6r0ynhw5z",
  "object": "event",
  "type": "invoice.sent",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "invoice",
    "id": "in_s5v1x8c3q9m2k7r4t0yhwn6z",
    "mode": "test",
    "number": "INV-000014",
    "status": "open",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": null,
    "billingReason": "manual",
    "periodStart": null,
    "periodEnd": null,
    "lines": [
      {
        "description": "Grade 10 maths, third term",
        "quantity": 1,
        "unitAmountCents": 1800000,
        "amountCents": 1800000,
        "periodStart": null,
        "periodEnd": null,
        "proration": false
      }
    ],
    "subtotalCents": 1800000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 1800000,
    "creditAppliedCents": 0,
    "amountDueCents": 1800000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "send_invoice",
    "dueDate": "2026-10-31T18:29:59.000Z",
    "memo": "Third term, grade 10",
    "currency": "LKR",
    "attemptCount": 0,
    "nextAttemptAt": null,
    "lastFailure": null,
    "paidAt": null,
    "hostedInvoiceUrl": "https://payments.lk/invoice/r2Vd9x4Kc7mZt3Wn1Rb8Hy6Lp5Gs0Jf4Ae2Uo9Ik7Nx",
    "createdAt": "2026-10-01T08:00:00.000Z"
  }
}

invoice.overdue

An invoice sent to be paid by a due date was not paid by then. Its subscription, if any, is past due. data is the invoice.

invoice.overdue example
{
  "id": "evt_i9o4v2x8c3q1m5k7r0hwnz6y",
  "object": "event",
  "type": "invoice.overdue",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "invoice",
    "id": "in_s5v1x8c3q9m2k7r4t0yhwn6z",
    "mode": "test",
    "number": "INV-000014",
    "status": "open",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": null,
    "billingReason": "manual",
    "periodStart": null,
    "periodEnd": null,
    "lines": [
      {
        "description": "Grade 10 maths, third term",
        "quantity": 1,
        "unitAmountCents": 1800000,
        "amountCents": 1800000,
        "periodStart": null,
        "periodEnd": null,
        "proration": false
      }
    ],
    "subtotalCents": 1800000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 1800000,
    "creditAppliedCents": 0,
    "amountDueCents": 1800000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "send_invoice",
    "dueDate": "2026-10-31T18:29:59.000Z",
    "memo": "Third term, grade 10",
    "currency": "LKR",
    "attemptCount": 0,
    "nextAttemptAt": null,
    "lastFailure": null,
    "paidAt": null,
    "hostedInvoiceUrl": "https://payments.lk/invoice/r2Vd9x4Kc7mZt3Wn1Rb8Hy6Lp5Gs0Jf4Ae2Uo9Ik7Nx",
    "createdAt": "2026-10-01T08:00:00.000Z"
  }
}

credit_note.created

A credit note was issued: an amount taken off an invoice, off what was due, refunded to the card, or added to the customer's credit balance. data is the credit note.

credit_note.created example
{
  "id": "evt_c9n5v3x9c4q2m6k8r0wnhy7z",
  "object": "event",
  "type": "credit_note.created",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "credit_note",
    "id": "cn_r7v2x9c4q1m8k3t6y0wnhz5p",
    "mode": "test",
    "invoiceId": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "number": "CN-000001",
    "amountCents": 150000,
    "reason": "order_change",
    "memo": "Closed for a week in October",
    "amountDueReducedCents": 0,
    "refundedCents": 0,
    "creditedToBalanceCents": 150000,
    "refundId": null,
    "createdAt": "2026-10-20T08:00:00.000Z"
  }
}

entitlements.active_entitlement_summary.updated

A customer's features changed: a subscription started, changed, paused or ended, or a product gained or lost a feature. data is every feature the customer has now.

entitlements.active_entitlement_summary.updated example
{
  "id": "evt_e1n6v4x1c5q3m7k9r0hwnz8y",
  "object": "event",
  "type": "entitlements.active_entitlement_summary.updated",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "entitlements.active_entitlement_summary",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "entitlements": [
      {
        "featureId": "feat_g2v7x9c5q1m8k3r6t0hwnz4y",
        "lookupKey": "gym"
      },
      {
        "featureId": "feat_p3v8x1c6q9m2k7r4t0ynhw5z",
        "lookupKey": "pool"
      }
    ]
  }
}

invoice.paid

An invoice was paid. Provision for the period it covers from this event. data is the invoice.

invoice.paid example
{
  "id": "evt_i3x2v9c8q1m4k7r6t0hwyn5z",
  "object": "event",
  "type": "invoice.paid",
  "mode": "test",
  "created": "2026-10-01T04:15:02.000Z",
  "data": {
    "object": "invoice",
    "id": "in_c4v9x2q7m1k8r3t6y0wnzhp5",
    "mode": "test",
    "number": "INV-000001",
    "status": "paid",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_create",
    "periodStart": "2026-10-01T04:12:31.000Z",
    "periodEnd": "2026-11-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-10-01T04:12:31.000Z",
        "periodEnd": "2026-11-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 450000,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 0,
    "nextAttemptAt": null,
    "lastFailure": null,
    "paidAt": "2026-10-01T04:15:02.000Z",
    "hostedInvoiceUrl": "https://payments.lk/invoice/q7Vd2x9Kc4mZt1Wn8Rb5Hy3Lp6Gs0Jf2Ae4Uo7Ik9Nx",
    "createdAt": "2026-10-01T04:12:31.000Z"
  }
}

invoice.payment_failed

A charge of the card on file failed, or there was no card to charge. data is the invoice, with attemptCount, lastFailure and nextAttemptAt (null when nothing more will be tried). The customer is emailed a pay link.

invoice.payment_failed example
{
  "id": "evt_i4c1v8x9q2m3k6r7t0nwhz5y",
  "object": "event",
  "type": "invoice.payment_failed",
  "mode": "test",
  "created": "2026-11-01T05:13:04.000Z",
  "data": {
    "object": "invoice",
    "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
    "mode": "test",
    "number": "INV-000002",
    "status": "open",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_cycle",
    "periodStart": "2026-11-01T04:12:31.000Z",
    "periodEnd": "2026-12-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-11-01T04:12:31.000Z",
        "periodEnd": "2026-12-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 1,
    "nextAttemptAt": "2026-11-02T03:30:00.000Z",
    "lastFailure": "The processor refused the charge.",
    "paidAt": null,
    "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
    "createdAt": "2026-11-01T04:13:00.000Z"
  }
}

invoice.upcoming

A renewal is a few days away (three unless set otherwise). data is a preview of the invoice it will make; its id is not an invoice's.

invoice.upcoming example
{
  "id": "evt_i5v2x8c1q9m4k3r6t0yhzw7n",
  "object": "event",
  "type": "invoice.upcoming",
  "mode": "test",
  "created": "2026-10-29T04:13:00.000Z",
  "data": {
    "object": "invoice",
    "id": "upcoming_sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "mode": "test",
    "number": null,
    "status": "draft",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_cycle",
    "periodStart": "2026-11-01T04:12:31.000Z",
    "periodEnd": "2026-12-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-11-01T04:12:31.000Z",
        "periodEnd": "2026-12-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 0,
    "nextAttemptAt": null,
    "lastFailure": null,
    "paidAt": null,
    "hostedInvoiceUrl": null,
    "createdAt": "2026-10-29T04:13:00.000Z"
  }
}

invoice.voided

An invoice was voided, because its subscription ended or expired before it was paid. data is the invoice.

invoice.voided example
{
  "id": "evt_i6x9v1c2q8m3k7r4t0zwyh5n",
  "object": "event",
  "type": "invoice.voided",
  "mode": "test",
  "created": "2026-10-20T08:00:00.000Z",
  "data": {
    "object": "invoice",
    "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
    "mode": "test",
    "number": "INV-000002",
    "status": "void",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_cycle",
    "periodStart": "2026-11-01T04:12:31.000Z",
    "periodEnd": "2026-12-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-11-01T04:12:31.000Z",
        "periodEnd": "2026-12-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 1,
    "nextAttemptAt": null,
    "lastFailure": "The processor refused the charge.",
    "paidAt": null,
    "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
    "createdAt": "2026-11-01T04:13:00.000Z"
  }
}

invoice.marked_uncollectible

An invoice was written off: marked uncollectible by you, or when a subscription was canceled for non payment. It is never tried again. data is the invoice.

invoice.marked_uncollectible example
{
  "id": "evt_i7c3v1x9q8m2k5r4t0nhyw6z",
  "object": "event",
  "type": "invoice.marked_uncollectible",
  "mode": "test",
  "created": "2026-11-15T09:30:00.000Z",
  "data": {
    "object": "invoice",
    "id": "in_x7c2v9q4m1k6r8t3y0hwnzp5",
    "mode": "test",
    "number": "INV-000002",
    "status": "uncollectible",
    "customerId": "cus_q8w3n5v1x7c2m9r4t6ya0kzp",
    "subscriptionId": "sub_p6x1c8v3q9m2k7r4t0yhwz5n",
    "billingReason": "subscription_cycle",
    "periodStart": "2026-11-01T04:12:31.000Z",
    "periodEnd": "2026-12-01T04:12:31.000Z",
    "lines": [
      {
        "description": "Gym membership, every month",
        "quantity": 1,
        "unitAmountCents": 450000,
        "amountCents": 450000,
        "periodStart": "2026-11-01T04:12:31.000Z",
        "periodEnd": "2026-12-01T04:12:31.000Z",
        "proration": false
      }
    ],
    "subtotalCents": 450000,
    "discountCents": 0,
    "couponId": null,
    "taxCents": 0,
    "taxes": [],
    "totalCents": 450000,
    "creditAppliedCents": 0,
    "amountDueCents": 450000,
    "amountPaidCents": 0,
    "creditNotesCents": 0,
    "collectionMethod": "charge_automatically",
    "dueDate": null,
    "memo": null,
    "currency": "LKR",
    "attemptCount": 1,
    "nextAttemptAt": null,
    "lastFailure": "The processor refused the charge.",
    "paidAt": null,
    "hostedInvoiceUrl": "https://payments.lk/invoice/b3Nq8Zt2Xv6Lm1Kc9Rw4Hy7Pd5Gs0Jf2Ae4Uo7Ik9Mx",
    "createdAt": "2026-11-01T04:13:00.000Z"
  }
}

API reference · Payments.lk