Language
Live sandbox. Press Run on any endpoint below to send a real request with the key above. No real money moves — every charge is simulated.

Debo Pay API Reference

The Debo Pay API is organized around REST. It has predictable resource-oriented URLs, accepts JSON request bodies, returns JSON responses, and uses standard HTTP verbs and status codes.

Debo Pay is your payment gateway

Integrate once with a single API key. Debo Pay exposes many payment methods under one gateway — you connect to Debo Pay only; you do not wire each bank or wallet API yourself. We route collections and payouts across 30+ Ethiopian banks and wallets (telebirr, M-PESA, CBE Birr, Ebirr, Kacha, EthSwitch banks, international cards, and more). Choose a payment_method on charge or let customers pick on hosted checkout; Debo Pay handles each rail behind the scenes.

BASE URL
https://debopay.com/v1

Integration Guide

Full step-by-step walkthrough: get keys → initialize payment → redirect to checkout → verify webhooks → go live. Separate copy-paste paths for plain PHP, Laravel, and Node.js (Express).

  1. Create sandbox keys on Dashboard → API Keys.
  2. Call POST /v1/payments/initialize from your server with a required Idempotency-Key.
  3. Redirect the customer to checkout_url.
  4. Verify X-DeboPay-Signature on your webhook, then mark the order paid.
  5. Switch to sk_live_ / whsec_live_ for production.

Plain PHP

SDK or raw cURL + webhook endpoint.

Laravel

Config, checkout controller, CSRF-exempt webhook.

Node.js

Express + raw body signature verify.

Authentication

DeboPay issues three keys per bundle (Dashboard → API Keys). Use the right key for the right layer:

Key Prefix Where to use Never
Publishable pk_test_ / pk_live_ Browser / mobile — identify your merchant for hosted checkout Cannot call secret API routes alone
Secret sk_test_ / sk_live_ Server only — Authorization: Bearer sk_… for /v1/* Never ship in JS / public repos
Webhook whsec_test_ / whsec_live_ Verify callbacks with HMAC-SHA256 (X-DeboPay-Signature) Do not use as Bearer token
  1. Create a bundle on Dashboard → API Keys.
  2. Reveal with account password — values stay until you leave the page.
  3. Store sk_ in server env, pk_ in client env, whsec_ in your webhook verifier.
  4. Roll and Delete unused are password-gated. Delete only works if the key was never used.

How the three keys work together

  1. pk_ — Put in your website/app config so hosted checkout and client SDKs know which merchant is collecting. Safe to expose in public env vars.
  2. sk_ — Put only on your server (CI4 DEBOPAY_SECRET_KEY, Laravel DEBOPAY_SECRET_KEY, Node env). Every POST /v1/payments/initialize, charge, refund, and balance call uses Authorization: Bearer sk_….
  3. whsec_ — When DeboPay POSTs payment events to your callback_url, verify X-DeboPay-Signature = HMAC-SHA256(rawBody, whsec_) before trusting the payload. Reject mismatches.

Typical flow: your server creates a payment with sk_ → customer pays on DeboPay checkout → DeboPay calls your webhook signed with whsec_ → you mark the order paid. Use pk_ only if you embed publishable widgets.

Roll rotates one key type (pk, sk, or whsec) after password confirm — update every integration that stored the old value. Delete unused removes a never-used bundle (all three) after password confirm. Used keys can only be revoked (disabled), not deleted.

Sandbox keys start with pk_test_ / sk_test_ / whsec_test_. Live keys use _live_. Never commit secrets to git.

Authenticate API requests with your secret key as a Bearer token. Legacy dp_test_ / dp_live_ secrets still work.

Idempotency (required on money-moving POSTs)

This is a targeted requirement on create/move-funds endpoints — not a full API rewrite. Retries with the same key are safe; missing keys return 422.

  • Send idempotency_key in JSON or header Idempotency-Key (either is accepted).
  • Required on: POST /v1/payments/initialize, POST /v1/payments, POST /v1/charges, and wallet /v1/wallet/topup|transfer|bills.
  • Length 16–128; charset [A-Za-z0-9_-]. New key per new business intent; reuse the same key only when retrying the same request.
  • Same merchant + same key → same payment resource. Duplicate merchant reference with a different key → 409 duplicate_reference.

Create a key at Dashboard → API Keys

Each generate creates one card with Publishable (pk_), Secret (sk_), and Webhook (whsec_). Reveal / Roll / Delete require your account password.

Authenticated request
curl https://debopay.com/v1/balance \
  -H "Authorization: Bearer sk_test_sandbox_demo_secret_2026xx"
$ch = curl_init('https://debopay.com/v1/balance');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer sk_test_sandbox_demo_secret_2026xx',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$client = \Config\Services::curlrequest();
$response = $client->get('https://debopay.com/v1/balance', [
    'headers' => ['Authorization' => 'Bearer sk_test_sandbox_demo_secret_2026xx'],
]);
$response = Http::withToken('sk_test_sandbox_demo_secret_2026xx')
    ->get('https://debopay.com/v1/balance');
const res = await fetch('https://debopay.com/v1/balance', {
  headers: { Authorization: 'Bearer sk_test_sandbox_demo_secret_2026xx' },
});
const data = await res.json();
// Client-side: use pk_ only. Never ship sk_ in React.
const pk = process.env.NEXT_PUBLIC_DEBOPAY_PK;
// Server actions / Route Handlers use sk_ via process.env.DEBOPAY_SECRET
final res = await http.get(
  Uri.parse('https://debopay.com/v1/balance'),
  headers: {'Authorization': 'Bearer sk_test_sandbox_demo_secret_2026xx'},
);
const res = await fetch('https://debopay.com/v1/balance', {
  headers: { Authorization: 'Bearer sk_test_sandbox_demo_secret_2026xx' },
});
const data = await res.json();
import requests

res = requests.get(
    'https://debopay.com/v1/balance',
    headers={'Authorization': 'Bearer sk_test_sandbox_demo_secret_2026xx'},
)
print(res.json())
req, _ := http.NewRequest("GET", "https://debopay.com/v1/balance", nil)
req.Header.Set("Authorization", "Bearer sk_test_sandbox_demo_secret_2026xx")
res, _ := http.DefaultClient.Do(req)

Webhook verification (whsec_)

DeboPay signs webhook bodies with HMAC-SHA256 using your whsec_… secret. Compare the X-DeboPay-Signature header to hash_hmac('sha256', \$rawBody, \$whsec) (PHP) / equivalent in Node, Laravel, CI4, Flutter backends.

curl -X POST https://yoursite.com/webhooks/debopay \
  -H "Content-Type: application/json" \
  -H "X-DeboPay-Signature: <hmac>" \
  -d '{"event":"payment.success","data":{...}}'

Errors & Status Codes

Debo Pay uses conventional HTTP status codes. Errors return a consistent JSON envelope with a machine-readable code.

CodeMeaning
200 OKRequest succeeded
201 CreatedResource created
400 Bad RequestInvalid parameters
401 UnauthorizedMissing or invalid API key
404 Not FoundResource does not exist
409 ConflictDuplicate reference or state conflict
422 UnprocessableValidation failed
429 Too Many RequestsRate limit exceeded
Error response
{
  "status": "error",
  "error": {
    "code": "duplicate_reference",
    "message": "Reference already used for this merchant.",
    "doc_url": "https://docs.debopay.com/errors/duplicate_reference"
  }
}
POST /v1/payments/initialize

Create a Payment

Store an order on DeboPay from your site (phone, amount, order/reference). Returns checkout_url + debo_reference. Money-moving POSTs require idempotency: official SDKs auto-generate a valid key if you omit one, but for retry safety you should store a real key per order and reuse it on retries. Example: Abel · phone 0912121212 · amount 5000 · reference 716181. After pay, we webhook your callback_url (Stripe-style). Always send return_url for hosted checkout return.

Example Request
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","description":"Abel — order 716181","return_url":"https://yoursite.com/checkout/success","callback_url":"https://yoursite.com/webhooks/debopay","customer":{"phone":"0912121212","first_name":"Abel"}}'
$order->debopay_idempotency_key ??= 'pay_716181_ab12cd34ef56gh78';

$debo->payments->initialize([
  'amount' => 5000,
  'currency' => 'ETB',
  'reference' => '716181',
  'idempotency_key' => $order->debopay_idempotency_key,
  'description' => 'Abel — order 716181',
  'return_url' => 'https://yoursite.com/checkout/success',
  'callback_url' => 'https://yoursite.com/webhooks/debopay',
  'customer' => ['phone' => '0912121212', 'first_name' => 'Abel'],
]);
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","description":"Abel — order 716181","return_url":"https://yoursite.com/checkout/success","callback_url":"https://yoursite.com/webhooks/debopay","customer":{"phone":"0912121212","first_name":"Abel"}}'
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","description":"Abel — order 716181","return_url":"https://yoursite.com/checkout/success","callback_url":"https://yoursite.com/webhooks/debopay","customer":{"phone":"0912121212","first_name":"Abel"}}'
const idempotencyKey = order.deboPayIdempotencyKey ?? 'pay_716181_ab12cd34ef56gh78';

await debo.payments.initialize({
  amount: 5000,
  currency: 'ETB',
  reference: '716181',
  idempotency_key: idempotencyKey,
  description: 'Abel — order 716181',
  return_url: 'https://yoursite.com/checkout/success',
  callback_url: 'https://yoursite.com/webhooks/debopay',
  customer: { phone: '0912121212', first_name: 'Abel' },
});
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","description":"Abel — order 716181","return_url":"https://yoursite.com/checkout/success","callback_url":"https://yoursite.com/webhooks/debopay","customer":{"phone":"0912121212","first_name":"Abel"}}'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/payments/initialize'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
const idempotencyKey = order.deboPayIdempotencyKey ?? 'pay_716181_ab12cd34ef56gh78';

await debo.payments.initialize({
  amount: 5000,
  currency: 'ETB',
  reference: '716181',
  idempotency_key: idempotencyKey,
  description: 'Abel — order 716181',
  return_url: 'https://yoursite.com/checkout/success',
  callback_url: 'https://yoursite.com/webhooks/debopay',
  customer: { phone: '0912121212', first_name: 'Abel' },
});
order.debopay_idempotency_key = order.debopay_idempotency_key or 'pay_716181_ab12cd34ef56gh78'

debo.payments.initialize(
  amount=5000, currency='ETB',
  reference='716181', idempotency_key=order.debopay_idempotency_key,
  description='Abel — order 716181',
  return_url='https://yoursite.com/checkout/success',
  callback_url='https://yoursite.com/webhooks/debopay',
  customer={'phone': '0912121212', 'first_name': 'Abel'},
)
idempotencyKey := order.DeboPayIdempotencyKey
if idempotencyKey == "" {
    idempotencyKey = "pay_716181_ab12cd34ef56gh78"
}

_, err := debo.Payments.Initialize(&debopay.PaymentParams{
  Amount:         5000,
  Currency:       "ETB",
  Reference:      "716181",
  IdempotencyKey: idempotencyKey,
  ReturnURL:      "https://yoursite.com/checkout/success",
  CallbackURL:    "https://yoursite.com/webhooks/debopay",
})
const idempotencyKey = order.deboPayIdempotencyKey ?? 'pay_716181_ab12cd34ef56gh78';

await debo.payments.initialize({
  amount: 5000,
  currency: 'ETB',
  reference: '716181',
  idempotency_key: idempotencyKey,
  description: 'Abel — order 716181',
  return_url: 'https://yoursite.com/checkout/success',
  callback_url: 'https://yoursite.com/webhooks/debopay',
  customer: { phone: '0912121212', first_name: 'Abel' },
});
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/payments/initialize")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/payments/initialize", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/payments/initialize")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/payments/initialize")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Try it live POST /v1/payments/initialize
Response
Successful Response Example
{
  "status": "success",
  "data": {
    "status": "Success",
    "reference": "DP240000001",
    "transaction": "TRX-987654321",
    "merchant": "Demo Coffee PLC",
    "customer": "Abebe Kebede",
    "amount": 250,
    "fee": 3,
    "currency": "ETB",
    "created": "2026-07-17",
    "settlement": "Completed",
    "payment_method": "Wallet",
    "checkout_url": "https://pay.debopay.com/checkout/DP240000001"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}

SuperApp pay by Order

SuperApp pay by Order lets any registered SuperApp (or DeboWallet) collect payment against your order_id — like an airline PNR. You create the order on DeboPay (POST /v1/payments/initialize or Business → Payment request) with customer info + amount. The customer searches that order id in the SuperApp, confirms details, and pays. DeboPay then hits your webhook with payment.success so your site marks the order complete. For classic web checkout, initialize and redirect the browser to checkout_url, then return the user to your app — the webhook still updates your backend.

Example: Abel · phone 0912121212 · amount 5000 · order 716181 · detail xxx.

1. Your site/API: store the order on DeboPay (reference = order_id).

2. Customer: SuperApp / Wallet → Payment request → search 716181 → Confirm & pay.

3. Your webhook: receive signed payment.success → fulfill (same idea as airlines completing a PNR).

Portal simulation: Business → Payment request → amount 1402, order 1xxj, description Abel → pay from wallet → status Paid.

POST /v1/charges

Direct Charge

Charge a customer directly through a specific provider (mobile money or card) without the hosted checkout. This is also a money-moving POST, so use the same idempotency rules: SDK auto-generation is supported, but explicit stored keys are recommended for retries.

Example Request
curl -X POST https://debopay.com/v1/charges \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: chg_001_ab12cd34ef56gh78" \
  -d '{"amount":2500,"reference":"chg_001","payment_method":"telebirr","phone":"+251911223344"}'
$debo->charges->create([
  'amount' => 2500,
  'reference' => 'chg_001',
  'idempotency_key' => 'chg_001_ab12cd34ef56gh78',
  'payment_method' => 'telebirr',
  'phone' => '+251911223344',
]);
curl -X POST https://debopay.com/v1/charges \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: chg_001_ab12cd34ef56gh78" \
  -d '{"amount":2500,"reference":"chg_001","payment_method":"telebirr","phone":"+251911223344"}'
curl -X POST https://debopay.com/v1/charges \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: chg_001_ab12cd34ef56gh78" \
  -d '{"amount":2500,"reference":"chg_001","payment_method":"telebirr","phone":"+251911223344"}'
await debo.charges.create({
  amount: 2500,
  reference: 'chg_001',
  idempotency_key: 'chg_001_ab12cd34ef56gh78',
  payment_method: 'telebirr',
  phone: '+251911223344',
});
curl -X POST https://debopay.com/v1/charges \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: chg_001_ab12cd34ef56gh78" \
  -d '{"amount":2500,"reference":"chg_001","payment_method":"telebirr","phone":"+251911223344"}'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/charges'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.charges.create({
  amount: 2500,
  reference: 'chg_001',
  idempotency_key: 'chg_001_ab12cd34ef56gh78',
  payment_method: 'telebirr',
  phone: '+251911223344',
});
debo.charges.create(
  amount=2500,
  reference='chg_001',
  idempotency_key='chg_001_ab12cd34ef56gh78',
  payment_method='telebirr',
  phone='+251911223344',
)
debo.Charges.Create(&debopay.ChargeParams{
  Amount:         2500,
  Reference:      "chg_001",
  IdempotencyKey: "chg_001_ab12cd34ef56gh78",
  PaymentMethod:  "telebirr",
  Phone:          "+251911223344",
})
await debo.charges.create({
  amount: 2500,
  reference: 'chg_001',
  idempotency_key: 'chg_001_ab12cd34ef56gh78',
  payment_method: 'telebirr',
  phone: '+251911223344',
});
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/charges")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/charges", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/charges")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/charges")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Try it live POST /v1/charges
Response
Successful Response Example
{
  "status": "success",
  "data": {
    "debo_reference": "DPX-20260529-A1B2C3",
    "amount": 2500,
    "status": "success",
    "payment_method": "telebirr",
    "provider_reference": "TELEBIRR-MUSWSD7XGJ"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}
GET /v1/transactions/{reference}

Verify a Transaction

Retrieve a transaction by your reference or the debo_reference to confirm its status.

Example Request
curl https://debopay.com/v1/transactions/order_001 \
  -H "Authorization: Bearer sk_test_..."
$debo->transactions->verify('order_001');
curl https://debopay.com/v1/transactions/order_001 \
  -H "Authorization: Bearer sk_test_..."
curl https://debopay.com/v1/transactions/order_001 \
  -H "Authorization: Bearer sk_test_..."
await debo.transactions.verify('order_001');
curl https://debopay.com/v1/transactions/order_001 \
  -H "Authorization: Bearer sk_test_..."
// Flutter (Dart)
final res = await http.get(
  Uri.parse('https://api.debopay.com/v1/transactions/{reference}'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.transactions.verify('order_001');
debo.transactions.verify('order_001')
debo.Transactions.Verify("order_001")
await debo.transactions.verify('order_001');
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/transactions/{reference}")
  .addHeader("Authorization", "Bearer sk_test_...")
  .get()
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.GetAsync("https://api.debopay.com/v1/transactions/{reference}");
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/transactions/{reference}")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/transactions/{reference}")!)
request.httpMethod = "GET"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Successful Response Example
{
  "status": "success",
  "data": {
    "reference": "order_001",
    "status": "success",
    "amount": 2500,
    "paid_at": "2026-05-29T08:12:55+00:00"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}
POST /v1/refunds

Refunds

Refund a successful payment fully or partially using the DeboPay transaction id (debo_reference from initialize/charge — e.g. DPX-20260717-A1B2C3). Do not use merchant order references, DeboFund campaign numbers, or system order IDs. Omit amount for a full refund.

Example Request
curl -X POST https://debopay.com/v1/refunds \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"transaction_id":"DPX-20260717-A1B2C3","amount":500,"reason":"partial"}'
$debo->refunds->create([
  'transaction_id' => 'DPX-20260717-A1B2C3',
  'amount' => 500,
]);
curl -X POST https://debopay.com/v1/refunds \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"transaction_id":"DPX-20260717-A1B2C3","amount":500,"reason":"partial"}'
curl -X POST https://debopay.com/v1/refunds \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"transaction_id":"DPX-20260717-A1B2C3","amount":500,"reason":"partial"}'
await debo.refunds.create({
  transaction_id: 'DPX-20260717-A1B2C3', amount: 500,
});
curl -X POST https://debopay.com/v1/refunds \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"transaction_id":"DPX-20260717-A1B2C3","amount":500,"reason":"partial"}'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/refunds'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.refunds.create({
  transaction_id: 'DPX-20260717-A1B2C3', amount: 500,
});
debo.refunds.create(
  transaction_id='DPX-20260717-A1B2C3', amount=500,
)
debo.Refunds.Create(&debopay.RefundParams{
  TransactionID: "DPX-20260717-A1B2C3", Amount: 500,
})
await debo.refunds.create({
  transaction_id: 'DPX-20260717-A1B2C3', amount: 500,
});
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/refunds")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/refunds", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/refunds")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/refunds")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Successful Response Example
{
  "status": "success",
  "data": {
    "debo_reference": "DPX-20260717-A1B2C3",
    "status": "success",
    "refunded_amount": 500,
    "currency": "ETB"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}
GET /v1/balance

Balance

Retrieve your available balance, total fees charged, and current fee rate.

Example Request
curl https://debopay.com/v1/balance -H "Authorization: Bearer sk_test_..."
$debo->balance->retrieve();
curl https://debopay.com/v1/balance -H "Authorization: Bearer sk_test_..."
curl https://debopay.com/v1/balance -H "Authorization: Bearer sk_test_..."
await debo.balance.retrieve();
curl https://debopay.com/v1/balance -H "Authorization: Bearer sk_test_..."
// Flutter (Dart)
final res = await http.get(
  Uri.parse('https://api.debopay.com/v1/balance'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.balance.retrieve();
debo.balance.retrieve()
debo.Balance.Retrieve()
await debo.balance.retrieve();
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/balance")
  .addHeader("Authorization", "Bearer sk_test_...")
  .get()
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.GetAsync("https://api.debopay.com/v1/balance");
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/balance")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/balance")!)
request.httpMethod = "GET"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Try it live GET /v1/balance
Response
Successful Response Example
{
  "status": "success",
  "data": {
    "available": 985.00,
    "currency": "ETB",
    "total_fees_charged": 15.00,
    "fee_percent": 1.5
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}
POST /v1/payouts

Payouts

Move funds from your Debo balance to any Ethiopian bank or wallet — instantly or on a schedule. Debo Pay handles local rail selection; you do not call external gateway APIs.

Example Request
curl -X POST https://debopay.com/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":500,"bank_code":"CBE","account_number":"1000123456789"}'
$debo->payouts->create(['amount' => 500]);
curl -X POST https://debopay.com/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":500,"bank_code":"CBE","account_number":"1000123456789"}'
curl -X POST https://debopay.com/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":500,"bank_code":"CBE","account_number":"1000123456789"}'
await debo.payouts.create({ amount: 500 });
curl -X POST https://debopay.com/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":500,"bank_code":"CBE","account_number":"1000123456789"}'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/payouts'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.payouts.create({ amount: 500 });
debo.payouts.create(amount=500)
debo.Payouts.Create(&debopay.PayoutParams{Amount: 500})
await debo.payouts.create({ amount: 500 });
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/payouts")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/payouts", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/payouts")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/payouts")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Successful Response Example
{
  "status": "success",
  "data": {
    "reference": "PO-20260529-HBSUPH5G",
    "amount": 500,
    "status": "paid",
    "bank_code": "CBE"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}
POST /v1/transfers/bulk

Bulk Transfers

Send up to 100 payouts in one request — ideal for salary payroll, donor refunds, or sweeping wallet float to your bank.

Example Request
curl -X POST https://debopay.com/v1/transfers/bulk \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"title":"Donor refunds","currency":"ETB","transfers":[{"account_number":"1000123456789","bank_code":"CBE","account_name":"Abebe K","amount":500,"reference":"ref_001"}]}'
$debo->transfers->bulk([
  'title' => 'Donor refunds',
  'currency' => 'ETB',
  'transfers' => [
    ['account_number' => '1000123456789', 'bank_code' => 'CBE', 'amount' => 500],
  ],
]);
curl -X POST https://debopay.com/v1/transfers/bulk \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"title":"Donor refunds","currency":"ETB","transfers":[{"account_number":"1000123456789","bank_code":"CBE","account_name":"Abebe K","amount":500,"reference":"ref_001"}]}'
curl -X POST https://debopay.com/v1/transfers/bulk \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"title":"Donor refunds","currency":"ETB","transfers":[{"account_number":"1000123456789","bank_code":"CBE","account_name":"Abebe K","amount":500,"reference":"ref_001"}]}'
await debo.transfers.bulk({ title: 'Donor refunds', currency: 'ETB', transfers: [{ account_number: '1000123456789', bank_code: 'CBE', amount: 500 }] });
curl -X POST https://debopay.com/v1/transfers/bulk \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"title":"Donor refunds","currency":"ETB","transfers":[{"account_number":"1000123456789","bank_code":"CBE","account_name":"Abebe K","amount":500,"reference":"ref_001"}]}'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/transfers/bulk'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.transfers.bulk({ title: 'Donor refunds', currency: 'ETB', transfers: [{ account_number: '1000123456789', bank_code: 'CBE', amount: 500 }] });
debo.transfers.bulk(title='Donor refunds', currency='ETB', transfers=[{'account_number': '1000123456789', 'bank_code': 'CBE', 'amount': 500}])
debo.Transfers.Bulk(debopay.BulkTransferParams{Title: "Donor refunds", Currency: "ETB"})
await debo.transfers.bulk({ title: 'Donor refunds', currency: 'ETB', transfers: [{ account_number: '1000123456789', bank_code: 'CBE', amount: 500 }] });
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/transfers/bulk")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/transfers/bulk", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/transfers/bulk")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/transfers/bulk")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Successful Response Example
{
  "status": "success",
  "data": {
    "batch_id": "BTX-20260608-ABC12345",
    "title": "Donor refunds",
    "currency": "ETB",
    "total": 1,
    "success": 1,
    "failed": 0,
    "results": [{"reference":"ref_001","account_number":"1000123456789","bank":"CBE","amount":500,"status":"success","transaction_id":"PO-20260608-XYZ"}]
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}

Webhooks

Register HTTPS endpoints to receive events such as payment.success, refund.completed and payout.paid. Every request is signed with X-DeboPay-Signature (HMAC-SHA256 of timestamp.body). Always verify the signature before trusting an event.

Verify a webhook signature
# Header format
X-DeboPay-Signature: t=1780042391,v1=62f5a1...

# Recompute: HMAC_SHA256(secret, "{t}.{raw_body}")
# and compare with v1 using a constant-time check.
$sig = hash_hmac('sha256', $timestamp.'.'.$payload, $secret);
if (! hash_equals($sig, $received)) abort(400);
# Header format
X-DeboPay-Signature: t=1780042391,v1=62f5a1...

# Recompute: HMAC_SHA256(secret, "{t}.{raw_body}")
# and compare with v1 using a constant-time check.
# Header format
X-DeboPay-Signature: t=1780042391,v1=62f5a1...

# Recompute: HMAC_SHA256(secret, "{t}.{raw_body}")
# and compare with v1 using a constant-time check.
const sig = crypto.createHmac('sha256', secret)
  .update(`${timestamp}.${rawBody}`).digest('hex');
if (sig !== received) throw new Error('bad signature');
# Header format
X-DeboPay-Signature: t=1780042391,v1=62f5a1...

# Recompute: HMAC_SHA256(secret, "{t}.{raw_body}")
# and compare with v1 using a constant-time check.
# Header format
X-DeboPay-Signature: t=1780042391,v1=62f5a1...

# Recompute: HMAC_SHA256(secret, "{t}.{raw_body}")
# and compare with v1 using a constant-time check.
const sig = crypto.createHmac('sha256', secret)
  .update(`${timestamp}.${rawBody}`).digest('hex');
if (sig !== received) throw new Error('bad signature');
sig = hmac.new(secret.encode(),
  f'{ts}.{body}'.encode(), hashlib.sha256).hexdigest()
assert hmac.compare_digest(sig, received)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "." + body))
ok := hmac.Equal(mac.Sum(nil), received)
Register an endpoint
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
curl -X POST https://debopay.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"url":"https://shop.et/webhooks","events":["payment.success"]}'
POST /v1/subscriptions

Recurring Payments

Create a subscription plan to charge a customer automatically on a daily, weekly, monthly or yearly cadence — or schedule outbound salary payroll in Super App (pairs with POST /transfers/bulk on each payment day). The scheduler charges each cycle and fires subscription.charged / subscription.charge_failed webhooks.

Example Request
curl -X POST https://debopay.com/v1/subscriptions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "plan_name": "Pro monthly",
    "amount": 499,
    "currency": "ETB",
    "interval": "monthly",
    "interval_count": 1,
    "total_cycles": 12,
    "payment_method": "telebirr",
    "customer": {"email": "buyer@shop.et", "phone": "+251911000111"}
  }'
$debo->subscriptions->create([
  'plan_name' => 'Pro monthly',
  'amount' => 499,
  'interval' => 'monthly',
  'total_cycles' => 12,
  'payment_method' => 'telebirr',
  'customer' => ['phone' => '+251911000111'],
]);
curl -X POST https://debopay.com/v1/subscriptions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "plan_name": "Pro monthly",
    "amount": 499,
    "currency": "ETB",
    "interval": "monthly",
    "interval_count": 1,
    "total_cycles": 12,
    "payment_method": "telebirr",
    "customer": {"email": "buyer@shop.et", "phone": "+251911000111"}
  }'
curl -X POST https://debopay.com/v1/subscriptions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "plan_name": "Pro monthly",
    "amount": 499,
    "currency": "ETB",
    "interval": "monthly",
    "interval_count": 1,
    "total_cycles": 12,
    "payment_method": "telebirr",
    "customer": {"email": "buyer@shop.et", "phone": "+251911000111"}
  }'
await debo.subscriptions.create({
  plan_name: 'Pro monthly',
  amount: 499,
  interval: 'monthly',
  total_cycles: 12,
  payment_method: 'telebirr',
  customer: { phone: '+251911000111' },
});
curl -X POST https://debopay.com/v1/subscriptions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "plan_name": "Pro monthly",
    "amount": 499,
    "currency": "ETB",
    "interval": "monthly",
    "interval_count": 1,
    "total_cycles": 12,
    "payment_method": "telebirr",
    "customer": {"email": "buyer@shop.et", "phone": "+251911000111"}
  }'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/subscriptions'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.subscriptions.create({
  plan_name: 'Pro monthly',
  amount: 499,
  interval: 'monthly',
  total_cycles: 12,
  payment_method: 'telebirr',
  customer: { phone: '+251911000111' },
});
debo.subscriptions.create(
  plan_name='Pro monthly',
  amount=499, interval='monthly',
  total_cycles=12, payment_method='telebirr',
  customer={'phone': '+251911000111'},
)
debo.Subscriptions.Create(&debopay.SubscriptionParams{
  PlanName: "Pro monthly", Amount: 499,
  Interval: "monthly", TotalCycles: 12,
  PaymentMethod: "telebirr",
})
await debo.subscriptions.create({
  plan_name: 'Pro monthly',
  amount: 499,
  interval: 'monthly',
  total_cycles: 12,
  payment_method: 'telebirr',
  customer: { phone: '+251911000111' },
});
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/subscriptions")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/subscriptions", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/subscriptions")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/subscriptions")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Try it live POST /v1/subscriptions
Response
Successful Response Example
{
  "status": "success",
  "data": {
    "reference": "sub_a1b2c3d4e5f6",
    "plan_name": "Pro monthly",
    "amount": 499,
    "currency": "ETB",
    "interval": "monthly",
    "interval_count": 1,
    "total_cycles": 12,
    "completed_cycles": 0,
    "status": "active",
    "next_run_at": "2026-06-03T00:00:00+00:00"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}

Manage a subscription

Retrieve, pause, resume or cancel a plan by its reference. Pausing keeps the plan but stops charges until resumed; cancelling is permanent.

Lifecycle endpoints
# List all subscriptions
curl https://debopay.com/v1/subscriptions -H "Authorization: Bearer sk_test_..."

# Retrieve one
curl https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6 -H "Authorization: Bearer sk_test_..."

# Pause / resume / cancel
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/pause  -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/resume -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/cancel -H "Authorization: Bearer sk_test_..."
$debo->subscriptions->all();
$debo->subscriptions->retrieve('sub_a1b2c3d4e5f6');
$debo->subscriptions->pause('sub_a1b2c3d4e5f6');
$debo->subscriptions->resume('sub_a1b2c3d4e5f6');
$debo->subscriptions->cancel('sub_a1b2c3d4e5f6');
# List all subscriptions
curl https://debopay.com/v1/subscriptions -H "Authorization: Bearer sk_test_..."

# Retrieve one
curl https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6 -H "Authorization: Bearer sk_test_..."

# Pause / resume / cancel
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/pause  -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/resume -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/cancel -H "Authorization: Bearer sk_test_..."
# List all subscriptions
curl https://debopay.com/v1/subscriptions -H "Authorization: Bearer sk_test_..."

# Retrieve one
curl https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6 -H "Authorization: Bearer sk_test_..."

# Pause / resume / cancel
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/pause  -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/resume -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/cancel -H "Authorization: Bearer sk_test_..."
await debo.subscriptions.list();
await debo.subscriptions.retrieve('sub_a1b2c3d4e5f6');
await debo.subscriptions.pause('sub_a1b2c3d4e5f6');
await debo.subscriptions.resume('sub_a1b2c3d4e5f6');
await debo.subscriptions.cancel('sub_a1b2c3d4e5f6');
# List all subscriptions
curl https://debopay.com/v1/subscriptions -H "Authorization: Bearer sk_test_..."

# Retrieve one
curl https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6 -H "Authorization: Bearer sk_test_..."

# Pause / resume / cancel
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/pause  -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/resume -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/cancel -H "Authorization: Bearer sk_test_..."
# List all subscriptions
curl https://debopay.com/v1/subscriptions -H "Authorization: Bearer sk_test_..."

# Retrieve one
curl https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6 -H "Authorization: Bearer sk_test_..."

# Pause / resume / cancel
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/pause  -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/resume -H "Authorization: Bearer sk_test_..."
curl -X POST https://debopay.com/v1/subscriptions/sub_a1b2c3d4e5f6/cancel -H "Authorization: Bearer sk_test_..."
await debo.subscriptions.list();
await debo.subscriptions.retrieve('sub_a1b2c3d4e5f6');
await debo.subscriptions.pause('sub_a1b2c3d4e5f6');
await debo.subscriptions.resume('sub_a1b2c3d4e5f6');
await debo.subscriptions.cancel('sub_a1b2c3d4e5f6');
debo.subscriptions.list()
debo.subscriptions.retrieve('sub_a1b2c3d4e5f6')
debo.subscriptions.pause('sub_a1b2c3d4e5f6')
debo.subscriptions.resume('sub_a1b2c3d4e5f6')
debo.subscriptions.cancel('sub_a1b2c3d4e5f6')
debo.Subscriptions.List()
debo.Subscriptions.Retrieve("sub_a1b2c3d4e5f6")
debo.Subscriptions.Pause("sub_a1b2c3d4e5f6")
debo.Subscriptions.Resume("sub_a1b2c3d4e5f6")
debo.Subscriptions.Cancel("sub_a1b2c3d4e5f6")

Parameters

FieldTypeDescription
plan_namestringHuman-readable plan label. Required.
amountnumberAmount charged each cycle. Required.
currencystring3-letter code. Defaults to ETB.
intervalstringdaily, weekly, monthly or yearly. Required.
interval_countintegerNumber of intervals between charges. Defaults to 1.
total_cyclesintegerStop after N charges. Omit for open-ended.
payment_methodstringProvider used for each charge (e.g. telebirr).
starts_atdatetimeWhen the first charge runs. Defaults to now.
customer.email / customer.phonestringCustomer contact for receipts & charges.

Subscription webhook events

EventFired when
subscription.chargedA cycle was charged successfully.
subscription.charge_failedA cycle charge failed (retried on the next run).
subscription.completedThe final cycle of a capped plan was charged.
subscription.cancelledThe subscription was cancelled.
POST /v1/fund/campaigns/{id}/donate

Debo Fund

Create crowdfunding campaigns and accept donations. Donations are charged through any supported provider.

Example Request
curl -X POST https://debopay.com/v1/fund/campaigns/{slug}/donate \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":600,"payment_method":"telebirr","donor_name":"Abebe","donor_phone":"+251911000111"}'
$debo->fund->donate($campaign, [
  'amount' => 600, 'payment_method' => 'telebirr',
  'donor_phone' => '+251911000111',
]);
curl -X POST https://debopay.com/v1/fund/campaigns/{slug}/donate \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":600,"payment_method":"telebirr","donor_name":"Abebe","donor_phone":"+251911000111"}'
curl -X POST https://debopay.com/v1/fund/campaigns/{slug}/donate \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":600,"payment_method":"telebirr","donor_name":"Abebe","donor_phone":"+251911000111"}'
await debo.fund.donate(campaign, {
  amount: 600, payment_method: 'telebirr',
  donor_phone: '+251911000111',
});
curl -X POST https://debopay.com/v1/fund/campaigns/{slug}/donate \
  -H "Authorization: Bearer sk_test_..." \
  -d '{"amount":600,"payment_method":"telebirr","donor_name":"Abebe","donor_phone":"+251911000111"}'
// Flutter (Dart)
final res = await http.post(
  Uri.parse('https://api.debopay.com/v1/fund/campaigns/{id}/donate'),
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json',
  },
);
print(res.body);
await debo.fund.donate(campaign, {
  amount: 600, payment_method: 'telebirr',
  donor_phone: '+251911000111',
});
debo.fund.donate(campaign,
  amount=600, payment_method='telebirr',
  donor_phone='+251911000111')
debo.Fund.Donate(campaign, &debopay.DonateParams{
  Amount: 600, PaymentMethod: "telebirr",
})
await debo.fund.donate(campaign, {
  amount: 600, payment_method: 'telebirr',
  donor_phone: '+251911000111',
});
// Java (OkHttp)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://api.debopay.com/v1/fund/campaigns/{id}/donate")
  .addHeader("Authorization", "Bearer sk_test_...")
  .post(body)
  .build();
Response response = client.newCall(request).execute();
// C#
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "sk_test_...");
var res = await client.PostAsync("https://api.debopay.com/v1/fund/campaigns/{id}/donate", content);
var json = await res.Content.ReadAsStringAsync();
// Kotlin
val client = OkHttpClient()
val request = Request.Builder()
  .url("https://api.debopay.com/v1/fund/campaigns/{id}/donate")
  .addHeader("Authorization", "Bearer sk_test_...")
  .build()
val response = client.newCall(request).execute()
// Swift
var request = URLRequest(url: URL(string: "https://api.debopay.com/v1/fund/campaigns/{id}/donate")!)
request.httpMethod = "POST"
request.setValue("Bearer sk_test_...", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)
Successful Response Example
{
  "status": "success",
  "data": {
    "donation_id": "019e723e-fe78-...",
    "amount": 600,
    "status": "success"
  }
}
Invalid API key response
{
  "status": "error",
  "error": {
    "code": "invalid_api_key",
    "message": "Authentication failed.",
    "doc_url": "https://docs.debopay.com/errors/invalid_api_key"
  }
}

SDKs & Libraries

Official libraries handle authentication, retries, auto-idempotency, and webhook verification for you. Every money-moving call (initializePayment, charge) auto-generates a 32-char idempotency key if you don't provide one. For retry safety, pass your own key — see the Idempotency section.

npm install https://api.debopay.com/downloads/sdks/debopay-node-1.0.0.tgz
PHP — existing project
composer config repositories.debopay composer https://api.debopay.com/downloads/composer && composer require debopay/debopay-php:^1.0
PHP — empty folder
printf '%s\n' '{"name":"app/app","require":{},"repositories":[{"type":"composer","url":"https://api.debopay.com/downloads/composer"}]}' > composer.json && composer require debopay/debopay-php:^1.0
pip install https://api.debopay.com/downloads/sdks/debopay-1.0.0.tar.gz
curl -fsSL https://api.debopay.com/downloads/sdks/debopay-go-1.0.0.zip -o /tmp/debopay-go.zip && mkdir -p third_party/debopay-go && unzip -qo /tmp/debopay-go.zip -d third_party/debopay-go && go mod edit -replace=github.com/debopay/debopay-go=./third_party/debopay-go && go get github.com/debopay/debopay-go@v1.0.0

SDKs are self-hosted on api.debopay.com until Packagist / npm / PyPI / GitHub modules are published. PHP: run from a project directory (not ~ alone). See SDK manifest.

Idempotency

Money-moving POST endpoints require an idempotency key to prevent duplicate charges. If a request times out or you receive a 5xx, retry with the same key and same body — DeboPay returns the original result instead of creating a duplicate.

Required on

EndpointWhy
POST /v1/payments/initializeCreates a payment intent — duplicate calls would create multiple orders
POST /v1/chargesDirectly debits a customer
POST /v1/wallet/topupAdds funds to a wallet
POST /v1/wallet/transferP2P or business transfer
POST /v1/wallet/billsBill / utility payment

How to send

Send the key as an HTTP header or in the JSON body (either is accepted). For raw HTTP clients like cURL, fetch, axios, Java, C#, or mobile apps, the header is usually the cleanest option. For official SDKs, passing idempotency_key in params is the clearest pattern.

Idempotency key — header or body
# Option A: HTTP header
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181",...}'

# Option B: JSON body field
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","idempotency_key":"pay_716181_ab12cd34ef56gh78",...}'
// The PHP SDK auto-generates a key if you omit one:
$dp = new \DeboPay\DeboPay('sk_test_...');
$dp->initializePayment([
    'amount'   => 5000,
    'currency' => 'ETB',
    'reference' => '716181',
]);
// → SDK adds idempotency_key automatically

// To control the key yourself (recommended for retries):
$dp->initializePayment([
    'amount'          => 5000,
    'currency'        => 'ETB',
    'reference'       => '716181',
    'idempotency_key' => $order->debopay_idempotency_key,
]);
// CI4 — store key in session or DB:
$key = session('checkout_idempotency_key')
    ?? session()->set('checkout_idempotency_key', bin2hex(random_bytes(16)))
       ->get('checkout_idempotency_key');

$dp = new \DeboPay\DeboPay(env('DEBOPAY_SECRET_KEY'));
$dp->initializePayment([
    'amount'          => 5000,
    'currency'        => 'ETB',
    'reference'       => $orderId,
    'idempotency_key' => $key,
]);
// Laravel — store key on the order for safe retries:
$order->debopay_idempotency_key ??= Str::uuid()->toString();
$dp = new \DeboPay\DeboPay(config('debopay.secret_key'));
$dp->initializePayment([
    'amount'          => 5000,
    'currency'        => 'ETB',
    'reference'       => $order->id,
    'idempotency_key' => $order->debopay_idempotency_key,
]);
$order->save();
// The Node SDK auto-generates a key if you omit one:
const dp = new DeboPay('sk_test_...');
await dp.initializePayment({
  amount: 5000,
  currency: 'ETB',
  reference: '716181',
});
// → SDK adds idempotency_key automatically

// To control the key yourself (recommended for retries):
order.deboPayIdempotencyKey ??= 'pay_716181_ab12cd34ef56gh78';
await dp.initializePayment({
  amount: 5000,
  currency: 'ETB',
  reference: '716181',
  idempotency_key: order.deboPayIdempotencyKey,
});
# Option A: HTTP header
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181",...}'

# Option B: JSON body field
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","idempotency_key":"pay_716181_ab12cd34ef56gh78",...}'
# Option A: HTTP header
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_716181_ab12cd34ef56gh78" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181",...}'

# Option B: JSON body field
curl -X POST https://debopay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"amount":5000,"currency":"ETB","reference":"716181","idempotency_key":"pay_716181_ab12cd34ef56gh78",...}'
// Vanilla JS — generate/store one key per payment attempt:
const idempotencyKey = order.idempotencyKey ?? crypto.randomUUID().replace(/-/g, '');

const res = await fetch('https://debopay.com/v1/payments/initialize', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer sk_test_...',
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey,
  },
  body: JSON.stringify({ amount: 5000, currency: 'ETB', reference: '716181' }),
});
# The Python SDK auto-generates a key if you omit one:
from debopay import DeboPay
dp = DeboPay('sk_test_...')
dp.initialize_payment(
    amount=5000,
    currency='ETB',
    reference='716181',
)
# → SDK adds idempotency_key automatically

# To control the key yourself:
dp.initialize_payment(
    amount=5000,
    currency='ETB',
    reference='716181',
    idempotency_key=order.debopay_idempotency_key,
)
// Go SDK — pass key in params:
if order.DeboPayIdempotencyKey == "" {
    order.DeboPayIdempotencyKey = "pay_716181_ab12cd34ef56gh78"
}

debo.Payments.Initialize(&debopay.PaymentParams{
    Amount:         5000,
    Currency:       "ETB",
    Reference:      "716181",
    IdempotencyKey: order.DeboPayIdempotencyKey,
})

Key rules

RuleDetail
Length16–128 characters
Charset[A-Za-z0-9_-]
New intentNew order / new payment → new key (and usually new reference)
RetryTimeout, 5xx, double-click → same key + same body
TTLKeys expire after 24 hours
Missing / shortReturns 422 validation error

Idempotency vs Reference

FieldOwnerPurpose
referenceMerchantYour order ID — must be unique per new payment intent
idempotency_keyClientPrevents duplicate execution of the same HTTP request

Same merchant + same key → returns original result (200/201). Same reference + different key → 409 duplicate_reference.

SDK auto-idempotency

All official SDKs (PHP, Node, Python, Go) support idempotency. PHP, Node, and Python auto-generate a valid key if you omit idempotency_key; Go examples should pass it explicitly. For retry safety in every language, store the key on your order and pass it explicitly so retries reuse the same key.

Best practice: Generate the key when the order is created, store it in your database, and pass it on every attempt. This way network retries, user double-clicks, and queue re-deliveries all resolve to the same payment.

Rate Limits

Sandbox is limited to 100 requests/minute; production limits scale with your tier. Responses include X-RateLimit-Remaining. Exceeding the limit returns 429 Too Many Requests — back off and retry with exponential delay.

Ready to integrate?

Grab your sandbox keys and make your first call now.

Open sandbox