Quick Start

Set up your environment and make your first API call in minutes.

Quick Start

The ePay Business API lets you initialize payment sessions, track transaction status, and receive webhook notifications — all with a single API key.


Prerequisites

  • An ePay merchant account (open one here)
  • An API key from your dashboard under Developers → API Keys

Authentication

All requests must include your secret key in the Authorization header:

Authorization: Bearer sk_live_your_key_here

Your key prefix sets the mode automatically — sk_live_… targets live payments, sk_test_… targets the sandbox. You don't configure the mode separately.


Base URL

https://api.e-pay.et/v1

All endpoints below are relative to this base URL.


Install an SDK

Every example on this site is available as raw HTTP and as an official SDK — use the SDK / HTTP toggle above any code block to switch, and the choice follows you across the whole site.

The SDKs are thin: same endpoints, same field names, same response shapes. What they add is the tedious part — automatic retries with backoff on 429/5xx, one idempotency key reused across every retry of an initialize, local validation of amounts and phone numbers, cursor pagination as a loop, and constant-time webhook signature verification.

LanguagePackageInstall
Node.js, Bun, Deno@e-pay/nodenpm install @e-pay/node
NestJS@e-pay/nestjsnpm install @e-pay/nestjs
Python 3.9+epaypip install epay
PHP 8.1+, Laravelepay-et/php-sdkcomposer require epay-et/php-sdk
Go 1.22+github.com/epay-et/go-sdkgo get github.com/epay-et/go-sdk

Each client reads EPAY_SECRET_KEY from the environment, so there is nothing to pass in the common case:

EPAY_SECRET_KEY=sk_test_your_key_here
EPAY_WEBHOOK_SECRET=your_webhook_secret

See SDKs & Plugins for the full surface of each one.


Your first payment

Initialize a session, redirect the customer, then check the result.

Create a payment session

raw HTTP
npm install @e-pay/node
pip install epay
composer require epay-et/php-sdk
go get github.com/epay-et/go-sdk
curl https://api.e-pay.et/v1/transactions/initialize \
  -X POST \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -H "Content-Type: application/json" \
  -H "x-idempotency-key: order_001" \
  -d '{
    "amount": "250.00",
    "currencyCode": "ETB",
    "customerPhone": "+251911234567",
    "merchantReference": "order_001"
  }'
import { Epay } from '@e-pay/node';

const epay = new Epay(); // reads EPAY_SECRET_KEY

const session = await epay.payments.initialize(
  {
    amount: '250.00',
    currencyCode: 'ETB',
    customerPhone: '+251911234567',
    merchantReference: 'order_001',
  },
  { idempotencyKey: 'order_001' },
);
const res = await fetch('https://api.e-pay.et/v1/transactions/initialize', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_your_key_here',
    'Content-Type': 'application/json',
    'x-idempotency-key': 'order_001',
  },
  body: JSON.stringify({
    amount: '250.00',
    currencyCode: 'ETB',
    customerPhone: '+251911234567',
    merchantReference: 'order_001',
  }),
});

const session = await res.json();
from epay import Epay

epay = Epay()  # reads EPAY_SECRET_KEY

session = epay.payments.initialize(
    amount="250.00",
    currency_code="ETB",
    customer_phone="+251911234567",
    merchant_reference="order_001",
    idempotency_key="order_001",
)
import requests

res = requests.post(
    "https://api.e-pay.et/v1/transactions/initialize",
    headers={
        "Authorization": "Bearer sk_test_your_key_here",
        "x-idempotency-key": "order_001",
    },
    json={
        "amount": "250.00",
        "currencyCode": "ETB",
        "customerPhone": "+251911234567",
        "merchantReference": "order_001",
    },
    timeout=30,
)

session = res.json()
use Epay\Epay;

$epay = new Epay(); // reads EPAY_SECRET_KEY

$session = $epay->payments->initialize([
    'amount' => '250.00',
    'currencyCode' => 'ETB',
    'customerPhone' => '+251911234567',
    'merchantReference' => 'order_001',
], idempotencyKey: 'order_001');
$ch = curl_init('https://api.e-pay.et/v1/transactions/initialize');

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer sk_test_your_key_here',
        'Content-Type: application/json',
        'x-idempotency-key: order_001',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => '250.00',
        'currencyCode' => 'ETB',
        'customerPhone' => '+251911234567',
        'merchantReference' => 'order_001',
    ]),
]);

$session = json_decode(curl_exec($ch), true);
curl_close($ch);
client, err := epay.NewClient() // reads EPAY_SECRET_KEY
if err != nil {
	log.Fatal(err)
}

session, err := client.Payments.Initialize(ctx, epay.InitializeParams{
	Amount:            "250.00",
	CurrencyCode:      "ETB",
	CustomerPhone:     "+251911234567",
	MerchantReference: "order_001",
	IdempotencyKey:    "order_001",
})
if err != nil {
	log.Fatal(err)
}
payload, _ := json.Marshal(map[string]string{
	"amount":            "250.00",
	"currencyCode":      "ETB",
	"customerPhone":     "+251911234567",
	"merchantReference": "order_001",
})

req, _ := http.NewRequest(http.MethodPost,
	"https://api.e-pay.et/v1/transactions/initialize", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer sk_test_your_key_here")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-idempotency-key", "order_001")

res, err := http.DefaultClient.Do(req)
if err != nil {
	log.Fatal(err)
}
defer res.Body.Close()

var session struct {
	Reference   string `json:"reference"`
	CheckoutURL string `json:"checkoutUrl"`
}
json.NewDecoder(res.Body).Decode(&session)

Redirect the customer

The response contains a checkoutUrl and a reference to track the payment.

{
  "reference": "PAB12CD3420260813",
  "checkoutUrl": "https://checkout.epayethiopia.com/pay/PAB12CD3420260813",
  "status": "success",
  "expiresAt": "2026-08-13T12:30:00.000Z"
}

Redirect your customer to checkoutUrl. Save the reference — you'll use it to check status and reconcile.

Check the result

Poll for status or wait for a webhook event.

raw HTTP
curl https://api.e-pay.et/v1/transactions/PAB12CD3420260813 \
  -H "Authorization: Bearer sk_test_your_key_here"
const transaction = await epay.transactions.retrieve(session.reference);

if (transaction.status === 'completed') {
  const receipt = await epay.payments.verify(session.reference);
}
const res = await fetch(
  `https://api.e-pay.et/v1/transactions/${session.reference}`,
  { headers: { Authorization: 'Bearer sk_test_your_key_here' } },
);

const transaction = await res.json();
transaction = epay.transactions.retrieve(session["reference"])

if transaction["status"] == "completed":
    receipt = epay.payments.verify(session["reference"])
res = requests.get(
    f"https://api.e-pay.et/v1/transactions/{session['reference']}",
    headers={"Authorization": "Bearer sk_test_your_key_here"},
    timeout=30,
)

transaction = res.json()
$transaction = $epay->transactions->retrieve($session['reference']);

if ($transaction['status'] === 'completed') {
    $receipt = $epay->payments->verify($session['reference']);
}
$ch = curl_init('https://api.e-pay.et/v1/transactions/' . $session['reference']);

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_test_your_key_here'],
]);

$transaction = json_decode(curl_exec($ch), true);
curl_close($ch);
transaction, err := client.Transactions.Retrieve(ctx, session.Reference)
if err != nil {
	log.Fatal(err)
}

if transaction.Status == epay.StatusCompleted {
	receipt, err := client.Payments.Verify(ctx, session.Reference)
	_ = receipt
	_ = err
}
req, _ := http.NewRequest(http.MethodGet,
	"https://api.e-pay.et/v1/transactions/"+session.Reference, nil)
req.Header.Set("Authorization", "Bearer sk_test_your_key_here")

res, err := http.DefaultClient.Do(req)
if err != nil {
	log.Fatal(err)
}
defer res.Body.Close()

var transaction map[string]any
json.NewDecoder(res.Body).Decode(&transaction)
{
  "reference": "PAB12CD3420260813",
  "merchantReference": "order_001",
  "status": "completed",
  "amount": "250.00",
  "currencyCode": "ETB",
  "paidAt": "2026-08-13T11:47:00.000Z",
  "createdAt": "2026-08-13T11:30:00.000Z"
}

Test mode

Use your sk_test_… key to run sandbox payments without moving real money. Test mode is identical to live — same endpoints, same response shapes, same webhook events.

Never use your live key in development or CI. Keep secret keys out of version control.

See Test Accounts for magic phone numbers that trigger specific outcomes in the sandbox.


Next steps

On this page