Electricity topup (current)
The Current API is deprecated for new integrations. It stays online indefinitely for backward compatibility — existing wiring doesn't need to change — but new builds should target v1 Purchases instead. The v1 endpoint also covers airtime and water, not just electricity.
Two-phase flow. Validate to pre-check the meter and quote charges, then confirm to actually generate tokens. Retry is for self-managed recovery from timeouts.
| Method | Path | Idempotent? |
|---|---|---|
POST | /api/topup/validate | Safe — no state change |
POST | /api/topup/confirm | No — issues tokens / debits funds |
POST | /api/topup/retry | Safe — looks up + reattempts a prior failure |
Validate
POST /api/topup/validate
Authorization: Bearer <access_token>curl -X POST https://api.scratchpower.com/api/topup/validate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"beneficiary_phone_number": "26771436390",
"charges_on_top_flag": false,
"comment": "This is a comment.",
"currency": "BWP",
"date": "2024-06-18 04:10:59",
"debited_account_id": 2,
"external_reference": "00000001",
"hard_validate": false,
"language": "en-BW",
"meter_number": "04040404040",
"send_beneficiary_email": false,
"send_beneficiary_sms": false,
"send_requester_email": false,
"send_requester_sms": false,
"source_channel": "USSD",
"source_tag": "SPWMM"
}'const res = await fetch("https://api.scratchpower.com/api/topup/validate", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 100,
beneficiary_phone_number: "26771436390",
charges_on_top_flag: false,
currency: "BWP",
date: "2024-06-18 04:10:59",
debited_account_id: 2,
external_reference: "00000001",
hard_validate: false,
language: "en-BW",
meter_number: "04040404040",
send_beneficiary_email: false,
send_beneficiary_sms: false,
send_requester_email: false,
send_requester_sms: false,
source_channel: "USSD",
source_tag: "SPWMM",
}),
});
const quote = await res.json();resp = requests.post(
"https://api.scratchpower.com/api/topup/validate",
headers={"Authorization": f"Bearer {token}"},
json={
"amount": 100,
"beneficiary_phone_number": "26771436390",
"charges_on_top_flag": False,
"currency": "BWP",
"date": "2024-06-18 04:10:59",
"debited_account_id": 2,
"external_reference": "00000001",
"hard_validate": False,
"language": "en-BW",
"meter_number": "04040404040",
"send_beneficiary_email": False,
"send_beneficiary_sms": False,
"send_requester_email": False,
"send_requester_sms": False,
"source_channel": "USSD",
"source_tag": "SPWMM",
},
)
quote = resp.json()Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
source_channel | string | yes | WEB, SMART_APP, USSD, SMS_MO, BATCH |
language | string | yes | Locale tag (e.g. en-BW) |
source_tag | string | optional | Free-form gateway/source identifier |
debited_account_id | integer | yes | From /auth/me accounts[].id |
beneficiary_phone_number | string | yes | E.164 (e.g. 26771436390) |
meter_number | string | yes | The electricity meter (typically 11 or 13 digits) |
amount | number | yes | Amount in currency units |
currency | string | yes | ISO 4217 |
charges_on_top_flag | boolean | optional | true (default): operator pays charges on top of amount. false: charges deducted from amount. |
comment | string | optional | Free text |
external_reference | string | optional | Your reference — used for retries |
hard_validate | boolean | optional | When true, attempts to generate a dummy token; useful for end-to-end smoke tests |
send_requester_* / send_beneficiary_* | boolean | optional | Notification flags. Set all to false if you want to handle SMS/email yourself. |
Response
{
"id": 20,
"channel": "USSD",
"status": "ON_GOING",
"type": "TOPUP",
"operator": { "id": 6, "full_name": "ScratchPower Dealer", ... },
"requester": { "id": 6, "full_name": "ScratchPower Dealer", ... },
"payer": {},
"beneficiary": {
"id": 13,
"full_name": "26771436390",
"phone_number": "26771436390",
"address": "",
"status": "ACTIVE"
},
"total": "BWP 100.00",
"net": "BWP 100.00",
"charge": "BWP 0.00",
"commission": "BWP 0.00",
"entry": "BWP 100.00",
"meter_number": "04040404040",
"meter_details": "John Smith",
"metadata": {}
}{
"response_code": "810",
"response_message": "Electricity recharge validation failed: 500 - Your meter number has been blocked and cannot purchase electricity. Please contact BPC.",
"timestamp": 1718719194001,
"developer_message": "com.tocchae.scratchPower.exception.TopupIntegrationServiceException",
"errors": {}
}meter_details is the verified consumer name returned by BPC. Surface
it to the user before confirm so they can catch typos.
Confirm
POST /api/topup/confirm
Authorization: Bearer <access_token>Same request body as /validate. Response includes the
provider-issued token field. Single-token responses carry
the bare token value (the provider's description, e.g.
"Credit Token", is stripped server-side). Multi-token
responses (auto-key-change, etc.) carry a |-separated list of
Description: Token pairs with the descriptions preserved —
detect the multi-token shape by checking whether token contains
|. See Token format
on the v1 reference page for the full parsing pattern; the wire
format is identical between Current and v1.
{
"id": 26,
"channel": "USSD",
"status": "SUCCESSFUL",
"type": "TOPUP",
"reference": "TOP/2024/06/1871610006",
"total": "BWP 1000.00",
"net": "BWP 1000.00",
"charge": "BWP 0.00",
"commission": "BWP 0.00",
"entry": "BWP 1000.00",
"token": "KeyChange Token1: 8190 1047 0212 0545 2032|KeyChange Token2: 8190 1047 0112 0545 2032|Credit Token: 0404 0404 1980 0000 1917",
"meter_number": "04040404198",
"electricity_units": "191.7kWh",
"meter_details": "Simon Sais",
"metadata": {
"vat": "BWP 245.62",
"levy": "BWP 0.00",
"regulatory_fee": "BWP 0.00",
"charge": "BWP 1000.00",
"cost": "BWP 754.38",
"provider_meter_details": "SGC: 901047, TI: 2, KRN: 1",
"provider_transaction_identifier": "52086"
}
}Re-sending the same /confirm payload after a successful response
issues a brand-new transaction. Always store the returned id /
reference on success and avoid re-sending. For ambiguous timeouts
use /api/topup/retry (below) — never just re-send confirm.
Retry
When confirm times out (network drop, provider slow), you don't know
whether the transaction succeeded. /retry looks up your previous
attempt by meter_number + external_reference, asks the provider
whether it actually completed, and either returns the original token
or surfaces the genuine failure.
POST /api/topup/retry
Authorization: Bearer <access_token>curl -X POST https://api.scratchpower.com/api/topup/retry \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"date": "2024-06-18 04:10:59",
"external_reference": "00000008",
"language": "en-BW",
"meter_number": "04040404040",
"source_channel": "USSD",
"source_tag": "SPWMM"
}'Response shape mirrors /confirm. A successful response means the
original transaction completed — show the returned token to the user.
A failure means the transaction genuinely failed; you can issue a
fresh /confirm.
Notification flags
Every notification flag (send_requester_sms, send_beneficiary_sms,
send_requester_email, send_beneficiary_email) can be set to
false to suppress server-side SMS/email and let your integration
deliver the messaging itself.
Failed transaction handling
Two options:
- Automated managed retries — Scratch Power retries failed transactions in the background (up to 5 attempts over 30 minutes), notifies the customer via SMS, and refunds if all attempts fail.
- Self-managed retries — use
/api/topup/retryto verify the status with BPC before deciding to attempt another transaction.
Accounts default to self-managed. Email support@scratch-power.com to switch.