Developers
API documentation
Authenticate with an API key and verify Ethiopian payments over HTTPS from any language.
Authentication
Create a key in the dashboard (jc_…), then send it on every request. Either header works; sending both is the most reliable:
Authorization: Bearer jc_•••••••• X-Api-Key: jc_••••••••
Prefer Authorization: Bearer <key>. Some hosts (especially Apache) strip the
Authorization header before PHP — if you get 401 Unauthorized with a valid key,
also send X-Api-Key: <key> (same value). Do not send the raw key alone in
Authorization without the Bearer prefix.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /v1/me | Key + entitlements + usage |
| GET | /v1/providers | Supported banks & wallets |
| GET | /v1/usage | Quota remaining |
| POST | /v1/verify | Verify one payment |
| POST | /v1/verify/batch | Verify many payments |
| POST | /v1/ocr | OCR a receipt image (multipart) |
Verify example
Same POST /v1/verify call in common languages. Replace the key and reference with your own.
curl -X POST https://justverify.et/v1/verify \
-H "Authorization: Bearer jc_YOUR_API_KEY" \
-H "X-Api-Key: jc_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "telebirr",
"reference": "CE12345678",
"expected_amount": 1500
}'
<?php
$payload = json_encode([
'provider' => 'telebirr',
'reference' => 'CE12345678',
'expected_amount' => 1500,
]);
$ch = curl_init('https://justverify.et/v1/verify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer jc_YOUR_API_KEY',
'X-Api-Key: jc_YOUR_API_KEY',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $payload,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
print_r($data);
const res = await fetch('https://justverify.et/v1/verify', {
method: 'POST',
headers: {
Authorization: 'Bearer jc_YOUR_API_KEY',
'X-Api-Key': 'jc_YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: 'telebirr',
reference: 'CE12345678',
expected_amount: 1500,
}),
});
const data = await res.json();
console.log(data);
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
@Injectable()
export class JustVerifyService {
constructor(private readonly http: HttpService) {}
async verifyPayment(reference: string, amount: number) {
const { data } = await firstValueFrom(
this.http.post(
'https://justverify.et/v1/verify',
{
provider: 'telebirr',
reference,
expected_amount: amount,
},
{
headers: {
Authorization: 'Bearer jc_YOUR_API_KEY',
'X-Api-Key': 'jc_YOUR_API_KEY',
'Content-Type': 'application/json',
},
},
),
);
return data;
}
}
import requests
res = requests.post(
'https://justverify.et/v1/verify',
headers={
'Authorization': 'Bearer jc_YOUR_API_KEY',
'X-Api-Key': 'jc_YOUR_API_KEY',
'Content-Type': 'application/json',
},
json={
'provider': 'telebirr',
'reference': 'CE12345678',
'expected_amount': 1500,
},
timeout=45,
)
print(res.status_code, res.json())
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"provider": "telebirr",
"reference": "CE12345678",
"expected_amount": 1500,
})
req, _ := http.NewRequest("POST", "https://justverify.et/v1/verify", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer jc_YOUR_API_KEY")
req.Header.Set("X-Api-Key", "jc_YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(data))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
String json = """
{
"provider": "telebirr",
"reference": "CE12345678",
"expected_amount": 1500
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://justverify.et/v1/verify"))
.header("Authorization", "Bearer jc_YOUR_API_KEY")
.header("X-Api-Key", "jc_YOUR_API_KEY")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "jc_YOUR_API_KEY");
client.DefaultRequestHeaders.Add("X-Api-Key", "jc_YOUR_API_KEY");
var payload = new {
provider = "telebirr",
reference = "CE12345678",
expected_amount = 1500
};
var content = new StringContent(
JsonSerializer.Serialize(payload),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync("https://justverify.et/v1/verify", content);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
Tips
- Send
Authorization: BearerandX-Api-Keywith the same key so auth works even if a proxy stripsAuthorization. - Prefer an explicit
provider(e.g.cbe,telebirr) when you know the bank. - For modern CBE receipts, pass the full
receipt_url(mbreciept.cbe.com.et/v2-…). - Only successful verifies count against your monthly quota.
- Response headers include
X-Quota-Remainingand rate-limit counters.