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_hereYour 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/v1All 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.
| Language | Package | Install |
|---|---|---|
| Node.js, Bun, Deno | @e-pay/node | npm install @e-pay/node |
| NestJS | @e-pay/nestjs | npm install @e-pay/nestjs |
| Python 3.9+ | epay | pip install epay |
| PHP 8.1+, Laravel | epay-et/php-sdk | composer require epay-et/php-sdk |
| Go 1.22+ | github.com/epay-et/go-sdk | go 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_secretSee 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
npm install @e-pay/nodepip install epaycomposer require epay-et/php-sdkgo get github.com/epay-et/go-sdkcurl 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.
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
- Initialize a Payment — full request and response reference
- Webhooks — receive payment events without polling
- Verify a Payment — confirm completion before fulfilling an order