Payment Intents
Create, retrieve, list, and cancel payment intents. Understand the full lifecycle from creation to settlement.
Payment Intents
A payment intent represents a single payment request. It tracks the full lifecycle from creation through payment, and optionally through refund.
Status flow
PENDING ──────> PAID ──────> REFUNDED
│
├──────> CANCELLED (merchant cancel or 24h expiry)
│
└──────> FAILED (payment error)- PENDING -- intent created, waiting for customer payment
- PAID -- customer has paid, funds are in your merchant account
- REFUNDED -- one or more refunds have been issued
- CANCELLED -- cancelled by merchant or expired after 24 hours
- FAILED -- payment attempt failed
Create an intent
Idempotency required
All write endpoints require an X-Idempotency-Key header. Use a unique key per request (e.g. your order ID). Safe to retry on network errors.
POST /dev/intentsRequest body
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | Yes | Amount in centimes. 50000 = 500.00 MRU. Minimum: 1 centime. |
amountType | string | No | FIXED (default) or OPEN. Open lets customer choose amount. |
label | string | No | Description shown to customer. Max 200 characters. |
metadata | object | No | Key-value pairs (string values only). Max 50 keys, 500 chars per value. |
success_url | string | No | URL to redirect customer after payment (for Hosted Checkout). |
cancel_url | string | No | URL to redirect customer on cancellation. |
Response
| Field | Type | Description |
|---|---|---|
referenceNumber | string | Unique identifier, e.g. PI-3XXXXXXXXXXXXXX |
shortCode | string | 6-character alphanumeric code for short URLs |
code | string | 6-digit payment code for in-person payments |
codeExpiresAt | string | ISO 8601 expiration time for the payment code |
payUrl | string | Hosted Checkout URL: https://pay.elebne.ai/p/{shortCode} |
amount | integer | Amount in centimes (null for OPEN intents) |
amountType | string | FIXED or OPEN |
currency | string | Always MRU |
label | string | Payment description |
status | string | PENDING |
sandbox | boolean | true for test keys, false for live |
metadata | object | Your custom key-value pairs |
expiresAt | string | ISO 8601 expiration (24h from creation) |
createdAt | string | ISO 8601 creation timestamp |
curl -X POST https://api.elebne.ai/api/v1/dev/intents \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: order-5678" \
-d '{
"amount": 50000,
"label": "Abonnement mensuel",
"metadata": {
"customer_id": "cust_001",
"plan": "premium"
}
}'const response = await fetch('https://api.elebne.ai/api/v1/dev/intents', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_YOUR_KEY',
'Content-Type': 'application/json',
'X-Idempotency-Key': 'order-5678',
},
body: JSON.stringify({
amount: 50000,
label: 'Abonnement mensuel',
metadata: {
customer_id: 'cust_001',
plan: 'premium',
},
}),
});
const { data } = await response.json();
console.log(data.referenceNumber); // PI-3XXXXXXXXXXXXXX
console.log(data.code); // 482917
console.log(data.payUrl); // https://pay.elebne.ai/p/A1B2C3import requests
response = requests.post(
'https://api.elebne.ai/api/v1/dev/intents',
headers={
'Authorization': 'Bearer sk_test_YOUR_KEY',
'Content-Type': 'application/json',
'X-Idempotency-Key': 'order-5678',
},
json={
'amount': 50000,
'label': 'Abonnement mensuel',
'metadata': {
'customer_id': 'cust_001',
'plan': 'premium',
},
},
)
data = response.json()['data']
print(data['referenceNumber']) # PI-3XXXXXXXXXXXXXX
print(data['code']) # 482917
print(data['payUrl']) # https://pay.elebne.ai/p/A1B2C3Get an intent
Retrieve the current state of a payment intent. Use this to verify payment status after a redirect or webhook.
GET /dev/intents/{referenceNumber}Read-only endpoint
You can use either your secret key or publishable key for this endpoint.
curl https://api.elebne.ai/api/v1/dev/intents/PI-3XXXXXXXXXXXXXX \
-H "Authorization: Bearer pk_test_YOUR_KEY"const response = await fetch(
'https://api.elebne.ai/api/v1/dev/intents/PI-3XXXXXXXXXXXXXX',
{
headers: { 'Authorization': 'Bearer pk_test_YOUR_KEY' },
}
);
const { data } = await response.json();
console.log(data.status); // PAID
console.log(data.payment); // { amountPaid: 50000, paidAt: "...", method: "WALLET" }response = requests.get(
'https://api.elebne.ai/api/v1/dev/intents/PI-3XXXXXXXXXXXXXX',
headers={'Authorization': 'Bearer pk_test_YOUR_KEY'},
)
data = response.json()['data']
print(data['status']) # PAID
print(data['payment']) # { amountPaid: 50000, paidAt: "...", method: "WALLET" }Response for a paid intent:
{
"success": true,
"data": {
"referenceNumber": "PI-3XXXXXXXXXXXXXX",
"shortCode": "A1B2C3",
"type": "API",
"amount": 50000,
"amountType": "FIXED",
"currency": "MRU",
"label": "Abonnement mensuel",
"status": "PAID",
"sandbox": true,
"metadata": { "customer_id": "cust_001", "plan": "premium" },
"payment": {
"amountPaid": 50000,
"paidAt": "2026-04-04T10:35:00.000Z",
"method": "WALLET"
},
"refunds": [],
"totalAmountRefunded": 0,
"expiresAt": "2026-04-05T10:30:00.000Z",
"createdAt": "2026-04-04T10:30:00.000Z",
"updatedAt": "2026-04-04T10:35:00.000Z"
}
}List intents
Retrieve a paginated list of your payment intents with optional filters.
GET /dev/intents?status=PAID&page=1&limit=20&from=2026-01-01&to=2026-12-31| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | all | Filter by status: PENDING, PAID, REFUNDED, CANCELLED, FAILED |
from | string | -- | ISO 8601 date, inclusive start |
to | string | -- | ISO 8601 date, inclusive end |
page | integer | 1 | Page number (starts at 1) |
limit | integer | 20 | Results per page (max 100) |
curl "https://api.elebne.ai/api/v1/dev/intents?status=PAID&page=1&limit=10" \
-H "Authorization: Bearer sk_test_YOUR_KEY"const params = new URLSearchParams({
status: 'PAID',
page: '1',
limit: '10',
});
const response = await fetch(
`https://api.elebne.ai/api/v1/dev/intents?${params}`,
{
headers: { 'Authorization': 'Bearer sk_test_YOUR_KEY' },
}
);
const { data } = await response.json();
console.log(`${data.total} paid intents`);response = requests.get(
'https://api.elebne.ai/api/v1/dev/intents',
headers={'Authorization': 'Bearer sk_test_YOUR_KEY'},
params={'status': 'PAID', 'page': 1, 'limit': 10},
)
result = response.json()
print(f"{result['data']['total']} paid intents")Response:
{
"success": true,
"data": {
"items": [
{ "referenceNumber": "PI-3XXXXXXXXXXXXXX", "amount": 50000, "status": "PAID", "..." : "..." }
],
"page": 1,
"limit": 10,
"total": 42,
"hasMore": true
}
}Cancel an intent
Cancel a pending payment intent. Only intents with status PENDING can be cancelled.
POST /dev/intents/{referenceNumber}/cancelcurl -X POST https://api.elebne.ai/api/v1/dev/intents/PI-3XXXXXXXXXXXXXX/cancel \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "X-Idempotency-Key: cancel-order-5678"const response = await fetch(
'https://api.elebne.ai/api/v1/dev/intents/PI-3XXXXXXXXXXXXXX/cancel',
{
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_YOUR_KEY',
'X-Idempotency-Key': 'cancel-order-5678',
},
}
);
const { data } = await response.json();
console.log(data.status); // CANCELLEDresponse = requests.post(
'https://api.elebne.ai/api/v1/dev/intents/PI-3XXXXXXXXXXXXXX/cancel',
headers={
'Authorization': 'Bearer sk_test_YOUR_KEY',
'X-Idempotency-Key': 'cancel-order-5678',
},
)
data = response.json()
print(data['status']) # CANCELLEDExpiration
Unpaid intents automatically expire 24 hours after creation. When an intent expires:
- Status changes from
PENDINGtoCANCELLED - A
payment.expiredwebhook event is fired - The 6-digit payment code is released
You do not need to manually cancel expired intents.
Error responses
| Error code | HTTP | Description |
|---|---|---|
IDEMPOTENCY_KEY_REQUIRED | 400 | Missing X-Idempotency-Key header |
AMOUNT_OUT_OF_RANGE | 400 | Amount outside allowed limits |
METADATA_TOO_MANY_KEYS | 400 | Metadata has more than 50 keys |
METADATA_VALUE_TOO_LONG | 400 | A metadata value exceeds 500 characters |
INTENT_NOT_FOUND | 404 | Intent does not exist or belongs to another business |
INTENT_CANCEL_INVALID_STATUS | 400 | Intent is not in PENDING status |
INTENT_CANCEL_CONFLICT | 400 | Intent status changed concurrently |
Next steps
- Refunds -- refund paid intents partially or fully
- Webhooks & Events -- get notified when payment status changes
- Hosted Checkout -- redirect-based payment flow
Was this page helpful?