Abliner API · v1

Build with Abliner

Start with one API key, one balance call, and one webhook URL. This page is written to help you integrate in minutes without guessing the response format.

Overview

Introduction

Abliner gives you a single integration to collect money (Mobile Money, Cards, QR), send money (mobile wallets & bank accounts), and send Bulk SMS from your approved Sender ID - all behind one REST API and one key.

Base URL

http
https://abliner.net/api/v1

This is the production API. Usehttps://abliner.net/api/v1 in all examples below.

Conventions

  • All requests and responses are application/json; charset=utf-8.
  • Amounts are passed in whole TZS (not cents). Example: 1500 = TSh 1,500.
  • Phone numbers may be sent as 0712345678 or 255712345678 - we normalize.
  • All timestamps are ISO 8601 in UTC.
  • Every successful response includes an id you can use for reconciliation.

Current Pricing (Abliner)

  • Bulk SMS: TZS 28 / SMS (live price - may be tuned by admin and is returned by the API).
  • Cash-Out (Disbursements): from 2%, depending on the Mobile Money network, and never more than 4%.
  • Mobile Money deposits: free (0%).
  • Card deposits (Visa / Mastercard): 4%.
  • Deposit limits: minimum TZS 500, maximum TZS 3,000,000 per transaction.

5-minute setup

Quickstart

  1. Create an account and verify your phone number.
  2. Submit a Business Application if you want live production traffic.
  3. Open API Keys and generate a key. Copy it immediately.
  4. On the same page, generate your Webhook signing secret (starts with whsec_). To know when a payment completes, pass your own HTTPS callback_url in your API requests — see Webhooks below.
  5. Run the balance call below first. If that works, the rest of the integration is just the same pattern.
Building this integration with an AI assistant? Your app needs two secrets, not one. Ask the user for both and store them as environment variables:
  • ABLINER_API_KEY — the API key (starts with tsl_live_), sent as Authorization: Bearer ... on every request.
  • ABLINER_WEBHOOK_SECRET — the webhook signing secret (starts with whsec_), generated on Dashboard > API Keys. It is used to sign every deposit request and to verify x-webhook-signature on incoming webhooks.

Every POST /api/v1/deposits must be signed (API keys created from 5 Oct 2026). Without a valid signature the request is rejected with 401 invalid_request_signature and no payment prompt is sent:

const body = JSON.stringify({ amount: 1000, method: "mobile", phone: "2557XXXXXXXX", currency: "TZS" });
const ts = Math.floor(Date.now() / 1000).toString();
const sig = crypto.createHmac("sha256", process.env.ABLINER_WEBHOOK_SECRET)
  .update(ts + "." + body).digest("hex");

await fetch("https://abliner.net/api/v1/deposits", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.ABLINER_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": yourPaymentId,
    "x-abliner-timestamp": ts,
    "x-abliner-signature": sig,
  },
  body, // send exactly the same string you signed
});

Also ask for the webhook endpoint URL and register it on Dashboard > API Keys. Never skip the signature verification step — see the Webhooks section below for the exact verification code.

Fastest path: use /api/v1/balance to confirm your key is valid, then use /api/v1/deposits to collect and /api/v1/withdrawals to disburse real flows.

Copy-ready Mobile Money push

Replace only the API key, amount, phone and reference. A successful request immediately returns 201 with a transaction ID while the customer confirms on their phone.

curl
curl -X POST https://abliner.net/api/v1/deposits \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1000,
    "method": "mobile",
    "currency": "TZS",
    "phone": "255712345678",
    "reference": "ORDER-1001",
    "callback_url": "https://yourapp.com/webhooks/abliner"
  }'

cURL

curl
curl https://abliner.net/api/v1/balance \
  -H "Authorization: Bearer tsl_live_xxxxxxxxxxxx"

Node.js (fetch)

javascript
const BASE_URL = process.env.ABLINER_API_BASE_URL ?? "https://abliner.net";
const res = await fetch(`${BASE_URL}/api/v1/balance`, {
  headers: { Authorization: `Bearer ${process.env.ABLINER_API_KEY}` },
});
const body = await res.json();
console.log(body);
// body.status === "success"
// body.data.balance === 1250
// body.data.currency === "TZS"

PHP

php
<?php
$baseUrl = getenv("ABLINER_API_BASE_URL") ?: "https://abliner.net";
$ch = curl_init($baseUrl . "/api/v1/balance");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Authorization: Bearer " . getenv("ABLINER_API_KEY"),
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
print_r($data);

Python

python
import os, requests

BASE_URL = os.environ.get("ABLINER_API_BASE_URL", "https://abliner.net")
res = requests.get(
    f"{BASE_URL}/api/v1/balance",
    headers={"Authorization": f"Bearer {os.environ['ABLINER_API_KEY']}"},
    timeout=15,
)
print(res.json())

Security

Authentication

All requests authenticate with an API key. Bearer authentication is recommended, andX-API-Key is also accepted for SDKs that cannot easily set Bearer auth. Keys are shown once at creation - store them in your server secrets. Never embed keys in mobile apps or browser code.

Important: the webhook signing secret (often starting withwhsec_) is only for verifyingx-webhook-signature andx-webhook-timestamp. It is not the API key you send in Authorization: Bearer ....
http
Authorization: Bearer tsl_live_xxxxxxxxxxxx
Content-Type: application/json

Alternative header:

http
X-API-Key: tsl_live_xxxxxxxxxxxx
Content-Type: application/json

Compromised a key? Revoke it from /dashboard/api-keys and generate a new one - subsequent requests with the old key return 401 within seconds.

What to use where

  • API key: the value generated in your dashboard, sent as the Bearer token in every REST request.
  • Webhook signing secret: the separate secret shown on the API Keys page, used only to verify webhook signatures in your own server.

Endpoints

Wallet

GET/api/v1/balance

Get your live TZS wallet balance and remaining SMS credits.

json
{
  "status": "success",
  "data": {
    "balance": 1250,
    "currency": "TZS",
    "balances": { "TZS": 1250, "USD": 0, "KES": 0, "UGX": 0 }
  }
}
GET/api/v1/transactions?limit=50

Recent transaction history for deposits, disbursements and SMS top-ups. The current API returns the latest items directly, so you can render them immediately in your dashboard or admin panel.

json
{
  "status": "success",
  "data": [
    {
      "id": "tx_01HZ...",
      "type": "deposit",
      "method": "mobile",
      "amount_cents": 500000,
      "status": "completed",
      "created_at": "2026-05-31T08:21:00Z",
      "reference": "INV-001"
    }
  ]
}

Collect

Deposits

Deposit or Checkout - which one?

Deposit (/api/v1/deposits) charges a mobile money number, card or QR. For method: "mobile" the payer gets a USSD push on the phone - there is no page to open and payment_url is null.

Checkout (/api/v1/checkout) creates a hosted payment page. The payer opens checkout_url and pays from their Abliner balance using their account number + TES. No phone number is needed.

Every response now carries type (deposit or checkout) and flow (push or hosted_page) so your SDK never mixes them up.

Initiate a Mobile Money (USSD push), Card or Dynamic QR collection. Abliner uses the configured payment route. An accepted request is not proof that the prompt reached the phone or that payment completed; confirm payment through status lookup or callbacks.

POST/api/v1/deposits

Always send currency - the ISO code of the money you are collecting (for example TZS, UGX, USD). It must match the country of the phone number you charge. When omitted we default to TZS.

json
{
  "amount": 1000,
  "method": "mobile",
  "currency": "TZS",
  "phone": "255712345678",
  "reference": "ORDER-2026-001",
  "callback_url": "https://yourapp.com/webhooks/abliner"
}

Response - for mobile a USSD push is triggered immediately; for card / dynamic-qr you redirect the customer to payment_url.

json
{
  "status": "success",
  "data": {
    "id": "tx_01HZABCDEF...",
    "type": "deposit",
    "flow": "push",
    "status": "pending",
    "amount": 1000,
    "currency": "TZS",
    "method": "mobile",
    "payment_url": null,
    "reference": "pp3k8fa91c2b7d",
    "customer_reference": "ORDER-2026-001",
    "message": "Mobile money push sent. Ask the payer to approve the prompt on their phone."
  }
}

Minimum deposit is TZS 500 and maximum is TZS 3,000,000 per transaction. phone is required when method is mobile. Missing or invalid fields return 422 validation_failed naming the exact field - never a 502. Your reference may contain dashes; we return it as customer_reference and send an alphanumeric reference to the mobile network, which is what webhooks echo.

If the phone still has an active payment prompt, the API returns 409 ussd_session_busy. Ask the payer to approve or dismiss the existing prompt. If none is visible, wait for the mobile session to expire. Check the previous transaction before starting a new attempt with a new Idempotency-Key; never automatically send another push after a timeout. Do not use the phone number as your payment key.

Node.js example

javascript
const BASE_URL = process.env.ABLINER_API_BASE_URL ?? "https://abliner.net";
const res = await fetch(`${BASE_URL}/api/v1/deposits`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ABLINER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 1000,
    method: "mobile",
    currency: "TZS",
    phone: "255712345678",
    reference: "ORDER-2026-001",
    callback_url: "https://yourapp.com/webhooks/abliner",
  }),
});
const tx = await res.json();

Final status is delivered via webhook (transaction.completed or transaction.failed) - do not block on the synchronous response.

Hosted payment

Abliner Checkout

Create a hosted payment session from your server using an Abliner API key. Send your customer to the returned checkout_url. The customer enters their Abliner account number and 4-digit TES, confirms their loaded name, and approves the payment.

After a successful payment, Abliner sends checkout.paid to yourcallback_url and redirects the customer to your redirect_url. Keep the API key on your server; never create Checkout sessions directly from browser code.

POST/api/v1/checkout
json
{
  "amount": 500,
  "currency": "TZS",
  "title": "Aston Shop",
  "description": "Payment for order 1024",
  "reference": "ORDER-1024",
  "callback_url": "https://yourshop.com/webhooks/abliner",
  "redirect_url": "https://yourshop.com/orders/1024/complete",
  "expires_in_minutes": 120
}

The minimum TZS amount is TZS 500. amount is required. Currency defaults to TZS; the remaining fields are optional, but providecallback_url for reliable server confirmation and redirect_urlto return the customer to your website.

Create response

json
{
  "status": "success",
  "data": {
    "id": "3f70b8d4-...",
    "token": "a1b2c3d4...",
    "checkout_url": "https://abliner.net/checkout/a1b2c3d4...",
    "amount": 500,
    "currency": "TZS",
    "reference": "ORDER-1024",
    "status": "pending",
    "expires_at": "2026-09-13T03:00:00.000Z"
  }
}

Check payment status

GET/api/v1/checkout?reference=ORDER-1024

You may query with ?reference= or ?id=. Both calls require the same Bearer API key and only return sessions owned by that key's account.

Payment callback

json
{
  "event": "checkout.paid",
  "id": "3f70b8d4-...",
  "token": "a1b2c3d4...",
  "reference": "ORDER-1024",
  "amount": 500,
  "currency": "TZS",
  "payer_account": "1000000001",
  "payer_name": "CUSTOMER NAME",
  "paid_at": "2026-09-13T01:05:00.000Z"
}

Clear validation errors

If a required or invalid field is submitted, the API returns HTTP 422with the exact field and reason so you can correct the integration.

json
{
  "status": "error",
  "code": "validation_failed",
  "message": "Invalid or missing field 'amount': amount is required and must be a number.",
  "field": "amount"
}

Disburse

Disbursements

Disburse money to a mobile wallet or bank account.

POST/api/v1/withdrawals

Include currency (for example TZS, KES, UGX) so the payout is settled in the recipient's local money. Defaults to TZS.

Mobile wallet

json
{
  "amount": 5000,
  "method": "mobile",
  "currency": "TZS",
  "recipient": "255712345678",
  "reference": "PAYOUT-001"
}

Bank account

json
{
  "amount": 50000,
  "method": "bank",
  "recipient": "0150123456789",
  "bank_code": "CRDB",
  "account_name": "John Doe",
  "reference": "PAYOUT-002"
}

Supported bank codes: CRDB, NMB, NBC, EQUITY, EXIM, STANBIC, DTB, AKIBA, AZANIA, BOA.

Fee preview

GET/api/v1/withdrawals/preview?method=mobile&amount=50000
json
{
  "status": "success",
  "data": {
    "amount_tzs": 50000,
    "fee_tzs": 2500,
    "total_debited_tzs": 52500
  }
}

Verify

Name Lookup

Check the registered name of a mobile money number before you disburse money, so your customers always send to the right person.

GET/api/v1/lookup?phone=255712345678
bash
curl "https://abliner.net/api/v1/lookup?phone=255712345678" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

json
{
  "status": "success",
  "data": {
    "phone": "255712345678",
    "name": "JOHN JUMA DOE",
    "network": "vodacom"
  }
}

If the number cannot be verified you receive 404 with name_not_found - double-check the number before retrying. Accepts any common format (0712…, 712…, +255…).

BillPay

Control Numbers

Generate a one-time control number your customer can pay from any bank or mobile money app. Powered by ClickPesa BillPay - this feature stays available even when ClickPesa is not the active deposit gateway.

POST/api/v1/control-numbers
json
{
  "amount": 10000,
  "description": "Invoice INV-001",
  "payment_mode": "EXACT",
  "reference": "INV-001",
  "customer_name": "John Doe"
}

payment_mode may be EXACT (customer must pay the exact amount) or PARTIAL. customer_name is optional - when provided we create a named customer control number instead of an order one.

json
{
  "status": "success",
  "data": {
    "id": "tx_01HZ...",
    "control_number": "993412345678",
    "reference": "INV-001",
    "amount": 10000,
    "payment_mode": "EXACT",
    "description": "Invoice INV-001"
  }
}

Once the customer pays, the same webhook you configured for deposits fires (transaction.completed) and the wallet is credited automatically.

How your customer pays

Show these steps to your customers. Choose Mobile Money or Bank to see each channel.

How will the customer pay?

Messaging

Bulk SMS

Send transactional or marketing SMS using your approved Sender ID when you have one, or fall back to the default ABLINER sender. Credits are pre-paid from your wallet.

GET/api/v1/sms/balance
json
{
  "status": "success",
  "data": {
    "sms_credits": 1500,
    "rate_tzs": 28
  }
}
GET/api/v1/sms/sender-ids
json
[
  { "sender_id": "MYSHOP", "status": "approved" },
  { "sender_id": "PROMO",  "status": "pending" }
]
POST/api/v1/sms/send
json
{
  "recipient": "255712345678",
  "message": "Welcome MYSHOP! Order #1234 imepokelewa.",
  "sender_id": "MYSHOP"
}
POST/api/v1/sms/send-bulk
json
{
  "sender_id": "MYSHOP",
  "message": "Promo siku 3 tu - 20% punguzo!",
  "recipients": ["255712345678", "255756111222", "255744333444"]
}

PHP example

php
<?php
$baseUrl = getenv("ABLINER_API_BASE_URL") ?: "https://abliner.net";
$ch = curl_init($baseUrl . "/api/v1/sms/send");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Authorization: Bearer " . getenv("ABLINER_API_KEY"),
  "Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
  "recipient" => "255712345678",
  "message"   => "Hello from PHP!",
  "sender_id" => "MYSHOP",
]));
$res = json_decode(curl_exec($ch), true);

⚠️ You need SMS credits before sending. If you have an approved Sender ID, it will be used; otherwise the default ABLINER sender is used. Buy credits at /dashboard/sms.

Realtime

Webhooks

Required: set your webhook callback URL first.

Abliner only sends payment status updates to a URL you have given us. If you have not set one, no status is ever sent and transactions will look pending forever in your system — even when the customer has paid.

To fix this, do one of the following:

  1. Recommended: open your Abliner Dashboard > API Keys and save your HTTPS URL under Add Your URL callback. This URL is then used automatically for every transaction.
  2. Or pass callback_url in each API request (deposit, withdrawal, checkout). A per-request callback_url overrides the saved one.

After saving, every completed or failed payment is POSTed to your URL about 25 seconds after it finishes, signed with your webhook secret.

To receive final transaction updates, pass your own HTTPS callback_url in your API request. Abliner will POST JSON to that URL whenever a transaction reaches a final state. Each request is signed so you can verify it came from Abliner.

Find your webhook signing secret on Dashboard > API Keys. Tap Generate personal secret to get one unique to your account. Use it in your server only and never expose it in browser code.

This is your endpoint in your own app or system. You do not copy an Abliner callback URL back into your own webhook receiver.

transaction.completed

json
{
  "id": "evt_3f2a9c1e-7b4d-4e2a-9f10-2c6d8e1a5b77_completed",
  "event": "transaction.completed",
  "created_at": "2026-10-05T08:23:11.000Z",
  "data": {
    "id": "3f2a9c1e-7b4d-4e2a-9f10-2c6d8e1a5b77",
    "type": "deposit",
    "method": "mobile",
    "amount": 5000,
    "amount_cents": 500000,
    "currency": "TZS",
    "status": "completed",
    "reference": "ABL8F2K1Q9",
    "customer_reference": "UPATU-CH-1029",
    "provider_reference": "PRV-77812345",
    "recipient": null,
    "completed_at": "2026-10-05T08:23:11.000Z"
  }
}

transaction.failed

json
{
  "id": "evt_3f2a9c1e-7b4d-4e2a-9f10-2c6d8e1a5b77_failed",
  "event": "transaction.failed",
  "created_at": "2026-10-05T08:24:40.000Z",
  "data": {
    "id": "3f2a9c1e-7b4d-4e2a-9f10-2c6d8e1a5b77",
    "type": "deposit",
    "method": "mobile",
    "amount": 5000,
    "amount_cents": 500000,
    "currency": "TZS",
    "status": "failed",
    "reference": "ABL8F2K1Q9",
    "customer_reference": "UPATU-CH-1029",
    "provider_reference": null,
    "recipient": null,
    "completed_at": "2026-10-05T08:24:40.000Z"
  }
}
  • amount is in whole units (TZS 5000). amount_cents is the same value × 100.
  • reference is the Abliner reference; customer_reference is the reference you sent; provider_reference is the mobile network reference when available.
  • id (also sent as header x-webhook-id) is stable across retries. Store it and ignore events you already processed.

Possible events: transaction.completed, transaction.failed, withdrawal.completed, withdrawal.failed (disbursement events keep the withdrawal.* names for compatibility with existing integrations).

Signature verification (Node.js)

x-webhook-timestamp is Unix time in seconds. The signature in x-webhook-signature is the hex HMAC-SHA256 of${timestamp}.${rawBody} using your webhook signing secret. Every retry is signed again with a fresh timestamp. Reject timestamps older than 5 minutes.

javascript
import crypto from "crypto";

// Store this as an environment variable: ABLINER_WEBHOOK_SECRET=whsec_...
const secret = process.env.ABLINER_WEBHOOK_SECRET;

export function verify(req) {
  const signature = req.headers["x-webhook-signature"];
  const timestamp = req.headers["x-webhook-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const rawBody = req.rawBody.toString();
  const message = `${timestamp}.${rawBody}`;
  const expected = crypto.createHmac("sha256", secret).update(message).digest("hex");

  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(signature ?? "", "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Respond with any 2xx within 8 seconds. If not, we retry up to 2 more times (after about 2s and 5s). If all attempts fail, reconcile with GET /api/v1/transactions.

Avoiding double charges (Idempotency-Key)

Required for new API keys. Send a unique Idempotency-Key header on POST /api/v1/deposits (for example your own payment ID). API keys created from 5 Oct 2026 onward get 400 idempotency_key_required without it, and 400 webhook_secret_required (no push sent) until a webhook signing secret is generated in Dashboard → API Keys. If you retry with the same key within 24 hours:

  • Same amount and currency: you get the original transaction back (200, "idempotent_replay": true). No new USSD push is sent.
  • Different amount or currency: 409 idempotency_conflict.
  • First request still running: 409 request_in_progress. Retry after a few seconds.
  • A failed original attempt returns an error, not a successful push replay. A new payment attempt needs a new key; the same key never sends another push.
  • Without the header, every request creates a new payment, even when the reference repeats.
bash
curl -X POST https://abliner.net/api/v1/deposits \
  -H "Authorization: Bearer tsl_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: UPATU-PAY-1029" \
  -d '{"amount":5000,"method":"mobile","phone":"255712345678","currency":"TZS","reference":"UPATU-CH-1029","callback_url":"https://yourapp.com/webhooks/abliner"}'

Lookup after a timeout

If your request timed out before you got a transaction ID, look it up by your key or your reference:

bash
GET /api/v1/transactions?idempotency_key=UPATU-PAY-1029
GET /api/v1/transactions?customer_reference=UPATU-CH-1029
GET /api/v1/transactions?id=3f2a9c1e-7b4d-4e2a-9f10-2c6d8e1a5b77
json
{
  "status": "success",
  "data": [
    {
      "id": "3f2a9c1e-7b4d-4e2a-9f10-2c6d8e1a5b77",
      "type": "deposit",
      "method": "mobile",
      "amount": 5000,
      "amount_cents": 500000,
      "currency": "TZS",
      "status": "pending",
      "reference": "ABL8F2K1Q9",
      "customer_reference": "UPATU-CH-1029",
      "provider_reference": null,
      "created_at": "2026-10-05T08:22:58.000Z"
    }
  ]
}

An empty data array means no transaction was created and no push was sent, so it is safe to retry. Safest of all: retry with the same Idempotency-Key, which never charges twice.

Testing

There is no sandbox. Test with small live amounts (minimum TZS 500). You need an API key, and business verification when your account requires it.

Throughput

Rate Limits

API requests are rate-limited per API key:

  • 120 req/min on read endpoints (/balance, /transactions).
  • 60 req/min on payment endpoints (/deposits, /withdrawals).
  • 30 req/sec on /sms/send and 5 req/sec on /sms/send-bulk (max 1,000 recipients per call).

Daily collection limit

Each developer account can collect up to TZS 150,000,000 per day through the API (/deposits and /checkout). The limit resets at 00:00 East Africa Time. Requests above the remaining amount return 422 with code daily_limit_exceeded. Abliner stores and marketplace sales have no daily limit.

When you exceed a limit we return 429 Too Many Requests with these headers:

http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 12

Need higher limits? Reach support inside the dashboard - we'll lift them for Business-verified accounts.

Reliability

Errors

Every /api/v1/* response is always JSON - including authentication failures, validation failures and unexpected server errors. You can safely call response.json() on any status code. Error responses share this shape:

json
{
  "status": "error",
  "code": "insufficient_funds",
  "message": "Insufficient balance.",
  "retryable": false,
  "field": "amount",            // only for validation errors
  "transaction_id": "7683..."   // only when a transaction was created
}

retryable: true means the same request can succeed later (upstream rail hiccup). retryable: false means the request must be changed before retrying.

  • 400 invalid_json - body was empty or not a JSON object. Send Content-Type: application/json.
  • 401 unauthorized - missing, invalid or revoked API key
  • 402 insufficient_funds - wallet balance too low
  • 403 - account suspended, or API access locked until business verification is approved
  • 404 not_found - unknown id or reference
  • 422 validation_failed - a field is missing or invalid; field and errors tell you exactly which one
  • 422 amount_out_of_range - below the minimum (TZS 500 for deposits and Checkout) or above the maximum (TZS 3,000,000 per deposit)
  • 422 unsupported_request - the network, country or payment method is not enabled right now (for example a blocked mobile network)
  • 422 invalid_phone - the phone number is not a valid mobile money number for that country
  • 502 provider_error - the payment rail rejected or did not answer. Retry with backoff.
  • 503 gateway_not_configured - that rail is temporarily switched off on our side
  • 500 internal_error - unexpected error on our side; retry with backoff

Handling responses safely

Check the HTTP status and read code - do not branch on the message text, which may be reworded.

javascript
const res = await fetch(`${BASE_URL}/api/v1/deposits`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ABLINER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

const raw = await res.text();          // read raw text first
let body;
try {
  body = JSON.parse(raw);
} catch {
  throw new Error(`Unexpected non-JSON response (${res.status}): ${raw.slice(0, 200)}`);
}

if (!res.ok) {
  // body.code: validation_failed | insufficient_funds | provider_error | ...
  if (body.retryable) {
    // safe to retry the same request after a short delay
  }
  throw new Error(body.message);
}

const deposit = body.data;

Include the transaction_id (or your reference) when contacting support - it lets us locate the exact request in seconds.

Ready to go live?

Generate a production API key and start integrating.

Open API Keys