Skip to main content

Endpoints

MethodEndpointDescriptionRate Limit
GET/v1/bank/listList supported banks and their codes300 req/min
POST/v1/aggregator/userCreate or retrieve an end-user120 req/min
POST/v1/aggregator/user/:id/bank-accountAdd a bank account for an end-user60 req/min
GET/v1/aggregator/user/:id/bank-account/listList bank accounts for an end-user200 req/min
GET/v1/aggregator/quoteGet a locked FX rate120 req/min
POST/v1/aggregator/offrampCreate an offramp (USDT → LKR transfer)60 req/min
GET/v1/aggregator/offramp/:idGet offramp status300 req/min
GET/v1/aggregator/report/ledgerFloat balance ledger reconciliation report60 req/min
GET/v1/aggregator/report/offrampOfframp transaction reconciliation report60 req/min
The Offramp API is only available to merchants with the AGGREGATOR role. Contact [email protected] to enable this for your account.

Flow Overview

1

Look up bank codes

Call GET /v1/bank/list to get CEFTS bank codes. This is a one-time reference — cache the list and reuse it.
2

Create or retrieve end-user

Call POST /v1/aggregator/user once per end-user. Safe to call on every offramp — returns the existing record if the user already exists.
3

Register their bank account

Call POST /v1/aggregator/user/:id/bank-account to add a Sri Lankan bank account. Store the returned userBankId.
4

Lock an FX rate

Call GET /v1/aggregator/quote with the USDT amount. The returned fxLockId is valid for 60 seconds.
5

Initiate the transfer

Call POST /v1/aggregator/offramp with the fxLockId, userId, and userBankId.
6

Poll status or receive webhook

Call GET /v1/aggregator/offramp/:id to check status, or provide a webhookUrl to receive payment.completed / payment.failed events.
Steps 2 and 3 only need to run once per end-user and bank account. Both IDs are reusable across multiple offramps.

Authentication

All endpoints require HMAC authentication:
  • x-api-key: Your API key ID (e.g. ak_live_xxx)
  • x-timestamp: Current Unix timestamp in milliseconds
  • x-signature: HMAC-SHA256 signature

List Supported Banks

Returns all active Sri Lankan banks and their CEFTS codes. Use this to look up the bankCode required when registering a bank account.
GET /v1/bank/list

Example Request

curl "https://api.ceypay.io/v1/bank/list" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here"

Example Response

[
  { "code": 7056, "name": "Commercial Bank PLC" },
  { "code": 7083, "name": "Bank of Ceylon" },
  { "code": 7010, "name": "People's Bank" }
]

Response Fields

FieldTypeDescription
codenumberCEFTS bank code — pass as bankCode when registering a bank account
namestringFull bank name

Create or Retrieve User

Upserts an end-user by your internal user ID. Returns the existing record if the user already exists.
POST /v1/aggregator/user

Request Body

FieldTypeRequiredDescription
externalUserIdstringYesUser ID in your system (max 255 chars)
CeyPay only stores the externalUserId you provide. No personal or identifying information about your end-users is collected or retained on our end.

Example Request

curl -X POST "https://api.ceypay.io/v1/aggregator/user" \
  -H "Content-Type: application/json" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here" \
  -d '{
    "externalUserId": "usr_1234567890"
  }'

Example Response

{
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "externalUserId": "usr_1234567890",
  "createdAt": "2026-05-28T10:00:00.000Z"
}

Response Fields

FieldTypeDescription
userIdstring (UUID)CeyPay’s internal user ID — use this in subsequent requests
externalUserIdstringThe ID you provided
createdAtstringWhen the user was first registered (ISO 8601)

Add Bank Account

Registers a Sri Lankan bank account for an end-user. Returns a userBankId used when creating offramps. A single end-user can have multiple bank accounts registered — each gets its own userBankId.
POST /v1/aggregator/user/:id/bank-account

Path Parameters

ParameterTypeDescription
idstring (UUID)Aggregator user ID from POST /v1/aggregator/user

Request Body

FieldTypeRequiredDescription
bankCodenumberYesCEFTS bank code from GET /v1/bank/list
accountNumberstringYesBank account number (max 100 chars)
accountNamestringYesAccount holder name (max 255 chars)
beneficiaryMobilestringNoBeneficiary mobile number (e.g. +94771234567)
beneficiaryEmailstringNoBeneficiary email address (max 255 chars)

Example Request

curl -X POST "https://api.ceypay.io/v1/aggregator/user/550e8400-e29b-41d4-a716-446655440000/bank-account" \
  -H "Content-Type: application/json" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here" \
  -d '{
    "bankCode": 7056,
    "accountNumber": "1234567890",
    "accountName": "John Doe"
  }'

Example Response

{
  "userBankId": "7f4e2100-a1b2-4c3d-8e9f-556677889900",
  "bankCode": 7056,
  "bankName": "Commercial Bank PLC",
  "accountNumber": "1234567890",
  "accountName": "John Doe",
  "beneficiaryMobile": "+94771234567",
  "beneficiaryEmail": "[email protected]",
  "createdAt": "2026-05-28T10:01:00.000Z"
}

Response Fields

FieldTypeDescription
userBankIdstring (UUID)Bank account ID — use this in POST /v1/aggregator/offramp
bankCodenumberCEFTS bank code
bankNamestringResolved bank name
accountNumberstringAccount number
accountNamestringAccount holder name
beneficiaryMobilestring | nullBeneficiary mobile number
beneficiaryEmailstring | nullBeneficiary email address
createdAtstringISO 8601 timestamp

List Bank Accounts

Returns all registered bank accounts for an end-user.
GET /v1/aggregator/user/:id/bank-account/list

Path Parameters

ParameterTypeDescription
idstring (UUID)Aggregator user ID

Example Request

curl "https://api.ceypay.io/v1/aggregator/user/550e8400-e29b-41d4-a716-446655440000/bank-account/list" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here"

Example Response

[
  {
    "userBankId": "7f4e2100-a1b2-4c3d-8e9f-556677889900",
    "bankCode": 7056,
    "bankName": "Commercial Bank PLC",
    "accountNumber": "1234567890",
    "accountName": "John Doe",
    "beneficiaryMobile": "+94771234567",
    "beneficiaryEmail": "[email protected]",
    "createdAt": "2026-05-28T10:01:00.000Z"
  }
]

Get a Quote

Fetches the current USDT/LKR exchange rate and locks it for 60 seconds. Use the returned fxLockId in POST /v1/aggregator/offramp before it expires.
GET /v1/aggregator/quote

Query Parameters

ParameterTypeRequiredDescription
amount_usdtnumberYesUSDT amount to convert (min: 0.00000001, max: 1,000,000)

Example Request

curl "https://api.ceypay.io/v1/aggregator/quote?amount_usdt=1000" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here"

Example Response

{
  "fxLockId": "a3b4c5d6-e7f8-4a9b-b0c1-d2e3f4a5b6c7",
  "rateUsdtLkr": 295.50,
  "amountUsdt": 1000.00,
  "amountLkr": 295500.00,
  "expiresAt": "2026-05-28T10:01:00.000Z"
}

Response Fields

FieldTypeDescription
fxLockIdstring (UUID)Rate lock ID — pass this to POST /v1/aggregator/offramp
rateUsdtLkrnumberLocked exchange rate (USDT per LKR)
amountUsdtnumberUSDT amount you requested
amountLkrnumberLKR equivalent at the locked rate
expiresAtstringWhen this rate lock expires — 60 seconds from issuance (ISO 8601)
Submit the offramp before expiresAt. Expired or already-used fxLockId values are rejected. Fetch a fresh quote if the lock expires before you submit.

Create Offramp

Initiates a USDT → LKR bank transfer. This endpoint is idempotent on externalRef — submitting the same key twice returns the original offramp unchanged.
POST /v1/aggregator/offramp

Request Body

FieldTypeRequiredDescription
fxLockIdstring (UUID)YesRate lock ID from GET /v1/aggregator/quote
userIdstring (UUID)YesEnd-user ID from POST /v1/aggregator/user
userBankIdstring (UUID)YesBank account ID from POST /v1/aggregator/user/:id/bank-account
externalRefstringYesYour idempotency key — must be unique per transfer (max 255 chars)
webhookUrlstringNoURL to receive payment.completed or payment.failed webhook

Example Request

curl -X POST "https://api.ceypay.io/v1/aggregator/offramp" \
  -H "Content-Type: application/json" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here" \
  -d '{
    "fxLockId": "a3b4c5d6-e7f8-4a9b-b0c1-d2e3f4a5b6c7",
    "userId": "550e8400-e29b-41d4-a716-446655440000",
    "userBankId": "7f4e2100-a1b2-4c3d-8e9f-556677889900",
    "externalRef": "withdrawal-9876543",
    "webhookUrl": "https://api.yourplatform.com/ceypay/webhook"
  }'

Example Response

{
  "paymentId": "c1d2e3f4-a5b6-4c7d-8e9f-001122334455",
  "status": "PENDING",
  "amountUsdt": 1000.00,
  "amountLkr": 295500.00,
  "rateUsdtLkr": 295.50,
  "externalRef": "withdrawal-9876543",
  "bankRef": null,
  "completedAt": null,
  "failedAt": null,
  "createdAt": "2026-05-28T10:00:45.000Z"
}

Response Fields

FieldTypeDescription
paymentIdstring (UUID)CeyPay’s offramp ID — use this to poll status
statusstringCurrent status (see Offramp Status)
amountUsdtnumberUSDT amount debited
amountLkrnumberLKR amount credited to the bank account
rateUsdtLkrnumberExchange rate applied
externalRefstringYour idempotency key
bankRefstring | nullBank transaction reference — available once COMPLETED
completedAtstring | nullISO 8601 timestamp when transfer completed
failedAtstring | nullISO 8601 timestamp when transfer failed
createdAtstringISO 8601 timestamp when offramp was created

Get Offramp

Retrieve the current status of an offramp.
GET /v1/aggregator/offramp/:id

Path Parameters

ParameterTypeDescription
idstring (UUID)Offramp paymentId

Example Request

curl "https://api.ceypay.io/v1/aggregator/offramp/c1d2e3f4-a5b6-4c7d-8e9f-001122334455" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here"

Example Response (Completed)

{
  "paymentId": "c1d2e3f4-a5b6-4c7d-8e9f-001122334455",
  "status": "COMPLETED",
  "amountUsdt": 1000.00,
  "amountLkr": 295500.00,
  "rateUsdtLkr": 295.50,
  "externalRef": "withdrawal-9876543",
  "bankRef": "BOC-TX-123456",
  "completedAt": "2026-05-28T10:05:00.000Z",
  "failedAt": null,
  "createdAt": "2026-05-28T10:00:45.000Z"
}

Error Responses

StatusDescription
403Offramp does not belong to your merchant account
404Offramp not found

Offramp Status

StatusDescription
PENDINGOfframp created, queued for processing
PROCESSINGBank transfer in progress
COMPLETEDTransfer settled — bankRef and completedAt are set
FAILEDTransfer failed — failedAt is set; see webhook failureReason

Webhooks

Set webhookUrl in your offramp request to receive a notification when the transfer settles or fails. Webhooks use the same ED25519 signature scheme as other CeyPay webhooks — see Webhook Integration Guide for signature verification details.

payment.completed

Fired when the bank transfer settles successfully.
{
  "event": "payment.completed",
  "paymentId": "c1d2e3f4-a5b6-4c7d-8e9f-001122334455",
  "externalRef": "withdrawal-9876543",
  "amountLkr": 295500.00,
  "bankRef": "BOC-TX-123456",
  "failureReason": null,
  "completedAt": "2026-05-28T10:05:00.000Z",
  "failedAt": null
}

payment.failed

Fired when the bank transfer cannot be completed.
{
  "event": "payment.failed",
  "paymentId": "c1d2e3f4-a5b6-4c7d-8e9f-001122334455",
  "externalRef": "withdrawal-9876543",
  "amountLkr": 295500.00,
  "bankRef": null,
  "failureReason": "Invalid account number",
  "completedAt": null,
  "failedAt": "2026-05-28T10:05:00.000Z"
}

Webhook Headers

X-Webhook-Signature: <base64-encoded ED25519 signature>
X-Webhook-Timestamp: 1748430300000
X-Webhook-Attempt: 1
Content-Type: application/json
User-Agent: CeyPay-Webhook/2.0

Retry Policy

AttemptDelay After Previous
1Immediate
21 minute
32 minutes
44 minutes
58 minutes
CeyPay retries on connection timeout, non-2xx responses, or network errors. Respond with 200 immediately and process the event asynchronously.

Idempotency

POST /v1/aggregator/offramp is idempotent on externalRef. If you submit the same key twice (e.g. after a network timeout), CeyPay returns the original offramp unchanged — no duplicate transfer is created. Use a stable, meaningful key such as your internal withdrawal ID:
"externalRef": "withdrawal-9876543"

Error Responses

StatusErrorDescription
400Bad RequestValidation error, expired FX quote, or already-used fxLockId
401UnauthorizedInvalid API key or HMAC signature
403ForbiddenOfframp belongs to another merchant, or account lacks Aggregator access
404Not FoundOfframp not found
429Too Many RequestsRate limit exceeded
See Error Codes for detailed error handling.

Reconciliation Reports

Two read-only endpoints are available for reconciling your float balance and offramp activity against your own records. Both endpoints are paginated and support date-range filtering.

Float Balance Ledger Report

Returns a paginated log of every LKR movement on your float (top-ups, offramp debits, and refunds) together with period summary statistics.
GET /v1/aggregator/report/ledger

Query Parameters

ParameterTypeRequiredDescription
pagenumberNoPage number, 1-indexed (default: 1)
limitnumberNoItems per page, max 100 (default: 50)
typestringNoFilter by entry type: CREDIT, DEBIT, or REFUND
startDatestringNoInclusive start date — ISO 8601 format, e.g. 2026-05-01
endDatestringNoInclusive end date — ISO 8601 format, e.g. 2026-05-31
sortOrderstringNoasc or desc (default: desc)

Example Request

curl "https://api.ceypay.io/v1/aggregator/report/ledger?startDate=2026-05-01&endDate=2026-05-31&limit=50" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here"

Example Response

{
  "pagination": {
    "currentPage": 1,
    "totalPages": 1,
    "totalCount": 13,
    "limit": 50,
    "hasNext": false,
    "hasPrev": false
  },
  "summary": {
    "currentBalanceLkr": 500000.00,
    "totalCreditsLkr": 750000.00,
    "totalDebitsLkr": 295500.00,
    "totalRefundsLkr": 0.00,
    "netMovementLkr": 454500.00,
    "creditCount": 3,
    "debitCount": 10,
    "refundCount": 0,
    "openingBalanceLkr": 45500.00,
    "closingBalanceLkr": 500000.00
  },
  "ledger": [
    {
      "id": "d4e5f6a7-b8c9-4d0e-1f2a-334455667788",
      "type": "DEBIT",
      "amountLkr": 295500.00,
      "balanceAfter": 500000.00,
      "aggregatorOfframpId": "c1d2e3f4-a5b6-4c7d-8e9f-001122334455",
      "bankRef": null,
      "notes": null,
      "createdAt": "2026-05-28T10:00:45.000Z"
    },
    {
      "id": "a1b2c3d4-e5f6-4a7b-8c9d-001122334455",
      "type": "CREDIT",
      "amountLkr": 1000000.00,
      "balanceAfter": 795500.00,
      "aggregatorOfframpId": null,
      "bankRef": "BOC-TOPUP-789",
      "notes": "May float top-up",
      "createdAt": "2026-05-02T08:00:00.000Z"
    }
  ]
}

Summary Fields

FieldTypeDescription
currentBalanceLkrnumberLive float balance at the time of the request
totalCreditsLkrnumberSum of all CREDIT entries in the date window
totalDebitsLkrnumberSum of all DEBIT entries in the date window
totalRefundsLkrnumberSum of all REFUND entries (failed offramp reversals) in the date window
netMovementLkrnumbercredits − debits + refunds for the period
creditCountnumberNumber of credit entries
debitCountnumberNumber of debit entries
refundCountnumberNumber of refund entries
openingBalanceLkrnumber | nullBalance snapshot immediately before startDate; null if no startDate or no prior entries
closingBalanceLkrnumber | nullBalance snapshot of the last entry on or before endDate; null if no endDate

Ledger Item Fields

FieldTypeDescription
idstring (UUID)Ledger entry ID
typestringCREDIT (admin top-up), DEBIT (offramp charge), or REFUND (failed offramp reversal)
amountLkrnumberLKR amount of this entry
balanceAfternumberFloat balance immediately after this entry was recorded
aggregatorOfframpIdstring | nullLinked offramp ID — set on DEBIT and REFUND entries
bankRefstring | nullBank transfer reference — set on CREDIT entries
notesstring | nullOptional notes added at the time of the entry
createdAtstringISO 8601 timestamp

Offramp Transaction Report

Returns a paginated list of offramps with per-status summary statistics and a weighted average FX rate. Each row includes the partner user ID and bank details so the report is self-contained for export.
GET /v1/aggregator/report/offramp

Query Parameters

ParameterTypeRequiredDescription
pagenumberNoPage number, 1-indexed (default: 1)
limitnumberNoItems per page, max 100 (default: 50)
statusstringNoFilter items by status: PENDING, PROCESSING, COMPLETED, or FAILED
startDatestringNoInclusive start date on created_at — ISO 8601 format
endDatestringNoInclusive end date on created_at — ISO 8601 format
sortBystringNocreated_at (default), completed_at, or amount_lkr
sortOrderstringNoasc or desc (default: desc)
The summary block always covers the full date window regardless of the status filter, so all per-status breakdowns remain visible even when the item list is filtered to a single status.

Example Request

curl "https://api.ceypay.io/v1/aggregator/report/offramp?startDate=2026-05-01&endDate=2026-05-31&status=COMPLETED" \
  -H "x-api-key: ak_live_abc123" \
  -H "x-timestamp: 1748430000000" \
  -H "x-signature: your_signature_here"

Example Response

{
  "pagination": {
    "currentPage": 1,
    "totalPages": 1,
    "totalCount": 12,
    "limit": 50,
    "hasNext": false,
    "hasPrev": false
  },
  "summary": {
    "totalCount": 15,
    "totalAmountLkr": 4425000.00,
    "totalAmountUsdt": 15000.00,
    "completedCount": 12,
    "completedAmountLkr": 3540000.00,
    "completedAmountUsdt": 12000.00,
    "pendingCount": 1,
    "pendingAmountLkr": 295500.00,
    "processingCount": 1,
    "processingAmountLkr": 295500.00,
    "failedCount": 1,
    "failedAmountLkr": 294000.00,
    "averageRateUsdtLkr": 295.00000000
  },
  "offramps": [
    {
      "paymentId": "c1d2e3f4-a5b6-4c7d-8e9f-001122334455",
      "externalRef": "withdrawal-9876543",
      "status": "COMPLETED",
      "amountUsdt": 1000.00,
      "amountLkr": 295500.00,
      "rateUsdtLkr": 295.50,
      "bankRef": "BOC-TX-123456",
      "failureReason": null,
      "externalUserId": "usr_1234567890",
      "accountNumber": "1234567890",
      "bankName": "Bank of Ceylon",
      "processedAt": "2026-05-28T10:02:00.000Z",
      "completedAt": "2026-05-28T10:05:00.000Z",
      "failedAt": null,
      "createdAt": "2026-05-28T10:00:45.000Z"
    }
  ]
}

Summary Fields

FieldTypeDescription
totalCountnumberTotal offramps in the date window (all statuses)
totalAmountLkrnumberSum of amountLkr across all statuses in the window
totalAmountUsdtnumberSum of amountUsdt across all statuses in the window
completedCountnumberNumber of COMPLETED offramps
completedAmountLkrnumberLKR total for completed offramps
completedAmountUsdtnumberUSDT total for completed offramps
pendingCountnumberNumber of PENDING offramps
pendingAmountLkrnumberLKR total for pending offramps
processingCountnumberNumber of PROCESSING offramps
processingAmountLkrnumberLKR total for processing offramps
failedCountnumberNumber of FAILED offramps
failedAmountLkrnumberLKR amount returned to float via refunds
averageRateUsdtLkrnumberWeighted average USDT/LKR rate for COMPLETED offramps only

Offramp Item Fields

FieldTypeDescription
paymentIdstring (UUID)CeyPay offramp ID
externalRefstringYour idempotency key
statusstringCurrent status (see Offramp Status)
amountUsdtnumberUSDT amount
amountLkrnumberLKR amount
rateUsdtLkrnumberExchange rate applied
bankRefstring | nullBank transaction reference — set when COMPLETED
failureReasonstring | nullFailure description — set when FAILED
externalUserIdstringYour internal user ID for the end-user
accountNumberstringDestination bank account number
bankNamestring | nullDestination bank name
processedAtstring | nullISO 8601 timestamp when processing began
completedAtstring | nullISO 8601 timestamp when transfer completed
failedAtstring | nullISO 8601 timestamp when transfer failed
createdAtstringISO 8601 timestamp when offramp was created