Balance
Read your MyTPE balance, top it up, and send funds to your customers one-by-one or in bulk.
Every trader has a single MyTPE balance in DZD — a prepaid, closed-loop merchant account used to fund customer refunds, payouts, and internal transfers. The external API lets you:
- Read the current balance ("sold").
- Initiate a top-up through SATIM (returns a hosted form URL you redirect your user to).
- Submit an offline top-up (bank transfer receipt or cheque photo) for admin approval.
- Send funds to a payer by email, phone, or user id — one at a time or in a batch.
Not a regulated e-wallet
The MyTPE balance is an internal merchant credit used only inside the MyTPE
ecosystem. It cannot be cashed out to third parties directly and is not a
regulated payment account. Some legacy fields and error codes still use the
prefix WALLET_* — treat them as opaque identifiers.
v1 rules
- Only traders can hold or fund a balance. API keys already belong to trader accounts, so the caller of these endpoints is always the account holder.
- Trader → trader transfers are disabled. Recipients must be non-trader users (payer accounts).
- Every write endpoint (
recharge,transfer,transfer/bulk) requires anX-Idempotency-Keyheader — retries with the same key are safe no-ops.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /wallet | Retrieve the caller's balance and status. |
POST | /wallet/recharge | Initiate a SATIM top-up. Returns a form URL. |
POST | /wallet/recharge/offline | Submit a bank-transfer / cheque proof for admin approval. |
POST | /wallet/transfer | Send funds to a single recipient. |
POST | /wallet/transfer/bulk | Send funds to many recipients. Partial success supported. |
The URL prefix /wallet is a legacy path — the endpoints power the MyTPE balance
described above. Do not rename it in your code; it's a stable public contract.
The balance object
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique identifier. |
currency | string | ISO code, always DZD today. |
balance | string | Balance as a decimal string, e.g. "1250.75". |
balance_centimes | integer | Balance in centimes (balance × 100), safe for arithmetic. |
status | string | active, frozen, or closed. |
version | integer | Optimistic-locking counter, bumped on every ledger write. |
created_at | string (ISO 8601) | Account creation timestamp. |
Get balance
curl https://api.mytpe.appp/api/ext/wallet \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{
"data": {
"id": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"currency": "DZD",
"balance": "1250.75",
"balance_centimes": 125075,
"status": "active",
"version": 42,
"created_at": "2026-05-01T09:00:00.000000Z"
}
}Recharge (top-up)
Kicks off a SATIM transaction and returns a hosted form URL. Redirect the trader to
form_url; on success their balance is credited automatically. The response's recharge.status
starts as pending and moves to succeeded once the gateway confirms.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
amount | integer | yes | In centimes (DA × 100). Must fall between recharge_min_amount and recharge_max_amount from the balance settings. |
Headers
| Header | Notes |
|---|---|
X-Idempotency-Key | Stable 8–128 char string. A retry with the same key returns the original recharge and form_url. |
Accept-Language | Optional. fr (default), en, or ar — drives the SATIM form language. |
curl -X POST https://api.mytpe.appp/api/ext/wallet/recharge \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET" \
-H "X-Idempotency-Key: 01JHV6MG9WK6QVBPT7RC2QYA0P" \
-H "Content-Type: application/json" \
-d '{ "amount": 500000 }'{
"data": {
"recharge": {
"id": "7c8d9e0f-1a2b-3c4d-5e6f-7a8b9c0d1e2f",
"wallet_id": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"amount": "5000.00",
"amount_centimes": 500000,
"status": "pending",
"transaction_id": "b4c5d6e7-f8a9-0b1c-2d3e-4f5a6b7c8d9e",
"created_at": "2026-06-15T10:22:00.000000Z"
},
"form_url": "https://cib.satim.dz/payment/merchants/mytpe/payment.html?...."
}
}Only traders can recharge
Non-trader accounts get 403 WALLET_RECHARGE_NOT_ALLOWED. In v1, payer users receive funds
only via a trader-initiated transfer.
Offline recharge (bank transfer / cheque)
Use this when the trader has already paid you via a bank wire, cheque, or any out-of-band channel and you want the balance credited without going through SATIM. You upload a proof file (receipt scan or cheque photo); an admin reviews it and, on approval, credits the balance. Nothing hits the ledger until the admin approves — a rejected recharge never touches the balance.
Request — multipart/form-data
| Field | Type | Required | Notes |
|---|---|---|---|
amount | integer | yes | Centimes. Subject to the same recharge_min_amount / recharge_max_amount as the online flow. |
proof | file | yes | jpg, jpeg, png, or pdf. Max 10 MB. Store one file per recharge. |
reference | string | no | ≤ 191 chars. Your bank wire id, cheque number, or any other correlation string — surfaced to the reviewing admin. |
Headers
| Header | Notes |
|---|---|
X-Idempotency-Key | Stable 8–128 char string. Retrying the same key returns the original recharge without creating a duplicate submission. |
Content-Type | multipart/form-data (curl sets this automatically when you use -F). |
curl -X POST https://api.mytpe.appp/api/ext/wallet/recharge/offline \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET" \
-H "X-Idempotency-Key: bank-wire-2026-07-19-A" \
-F "amount=500000" \
-F "reference=BNP-WIRE-2026-07-19-A" \
-F "proof=@/path/to/receipt.pdf"{
"data": {
"recharge": {
"id": "8d9e0f1a-2b3c-4d5e-6f7a-8b9c0d1e2f3a",
"wallet_id": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"amount": "5000.00",
"amount_centimes": 500000,
"method": "offline",
"status": "offline_pending",
"transaction_id": null,
"offline_reference": "BNP-WIRE-2026-07-19-A",
"proof_url": "https://api.mytpe.appp/storage/proofs/receipt.pdf",
"admin_notes": null,
"reviewed_at": null,
"created_at": "2026-07-19T14:22:00.000000Z"
}
}
}Offline recharge lifecycle
The status field transitions through:
| Status | Meaning |
|---|---|
offline_pending | Submitted, waiting for an admin to review the proof. Balance unchanged. |
succeeded | Admin approved. The balance has been credited with amount_centimes and reviewed_at is set. |
offline_rejected | Admin rejected. admin_notes carries the reason. Balance unchanged. Terminal — resubmit as a new recharge if needed. |
Poll GET /wallet/recharges/{id} (or list via GET /wallet/recharges?status=offline_pending) to observe the transition, or subscribe to the wallet.recharged webhook — it fires on the approval, exactly like an online recharge.
Common errors
| HTTP | error | When |
|---|---|---|
403 | WALLET_RECHARGE_NOT_ALLOWED | Caller is not a trader. |
422 | WALLET_RECHARGE_DISABLED | Recharges are globally disabled in settings. |
422 | WALLET_AMOUNT_OUT_OF_RANGE | amount outside the configured min/max. |
422 | validation | proof missing, wrong mime type, or over 10 MB. |
422 | WALLET_RECHARGE_NOT_PENDING_OFFLINE | You (or an admin acting on your behalf) tried to approve/reject a recharge that already left offline_pending. |
Never treat `offline_pending` as spendable
The offline_pending amount is not part of the balance. It only lands on
the ledger when an admin approves the recharge. Race conditions where you
kick off a transfer before approval will fail with WALLET_INSUFFICIENT_FUNDS.
Transfer to one user
Sends funds from the caller's balance to a single recipient. The recipient is resolved by exact
match against email, phone, or id.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
recipient | string | yes | Email, phone, or user id. |
amount | integer | yes | Centimes. Subject to transfer_min_amount / transfer_max_amount and the rolling 24 h transfer_daily_limit. |
note | string | no | ≤ 280 chars. Attached to the ledger row. |
curl -X POST https://api.mytpe.appp/api/ext/wallet/transfer \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET" \
-H "X-Idempotency-Key: transfer-invoice-4587" \
-H "Content-Type: application/json" \
-d '{
"recipient": "amina@example.com",
"amount": 150000,
"note": "Refund invoice #4587"
}'{
"data": {
"id": "6a5b4c3d-2e1f-0a9b-8c7d-6e5f4a3b2c1d",
"from_wallet_id": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"to_wallet_id": "1e2d3c4b-5a6f-7e8d-9c0b-1a2f3e4d5c6b",
"amount": "1500.00",
"amount_centimes": 150000,
"status": "completed",
"note": "Refund invoice #4587",
"created_at": "2026-06-15T11:00:00.000000Z"
}
}Common errors
| HTTP | error | When |
|---|---|---|
403 | WALLET_TRANSFER_SENDER_NOT_TRADER | The caller is not a trader (should not happen for API-key traffic). |
422 | WALLET_TRANSFER_RECIPIENT_IS_TRADER | The lookup resolved to a trader account (blocked in v1). |
404 | WALLET_RECIPIENT_NOT_FOUND | No user matches recipient. |
422 | WALLET_INSUFFICIENT_FUNDS | Balance can't cover the amount. |
422 | WALLET_TRANSFER_DAILY_LIMIT | Sum of transfers in the last 24 h exceeds the configured cap. |
422 | WALLET_SELF_TRANSFER | Recipient resolved to the caller. |
Transfer to many users (bulk)
Send funds to up to 100 recipients in one request. Each item is processed independently — failures on one row do not roll back successful rows. The response reports per-item outcomes and a summary count.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
transfers | array | yes | 1–100 items. |
transfers[].recipient | string | yes | Email, phone, or user id. |
transfers[].amount | integer | yes | Centimes. |
transfers[].note | string | no | ≤ 280 chars. |
transfers[].reference | string | no | ≤ 64 chars. Echoed back on the result row so you can correlate with your own DB (e.g. an invoice id). |
Idempotency: the caller supplies one batch X-Idempotency-Key; per-item keys are derived
server-side ({batchKey}:{index}). Re-sending the same batch is safe — already-processed rows
are returned as their original success result, no double debit.
curl -X POST https://api.mytpe.appp/api/ext/wallet/transfer/bulk \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET" \
-H "X-Idempotency-Key: payroll-2026-06-15" \
-H "Content-Type: application/json" \
-d '{
"transfers": [
{ "recipient": "amina@example.com", "amount": 150000, "reference": "INV-4587" },
{ "recipient": "yacine@example.com", "amount": 50000, "reference": "INV-4588" },
{ "recipient": "notfound@example.com","amount": 25000, "reference": "INV-4589" },
{ "recipient": "trader@another.com", "amount": 10000, "reference": "INV-4590" }
]
}'{
"data": {
"results": [
{
"index": 0,
"recipient": "amina@example.com",
"reference": "INV-4587",
"status": "success",
"transfer_id": "6a5b4c3d-2e1f-0a9b-8c7d-6e5f4a3b2c1d",
"amount_centimes": 150000
},
{
"index": 1,
"recipient": "yacine@example.com",
"reference": "INV-4588",
"status": "success",
"transfer_id": "b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e",
"amount_centimes": 50000
},
{
"index": 2,
"recipient": "notfound@example.com",
"reference": "INV-4589",
"status": "failed",
"error_code": "WALLET_RECIPIENT_NOT_FOUND",
"message": "No MyTPE user matches this identifier."
},
{
"index": 3,
"recipient": "trader@another.com",
"reference": "INV-4590",
"status": "failed",
"error_code": "WALLET_TRANSFER_RECIPIENT_IS_TRADER",
"message": "Trader-to-trader transfers are disabled."
}
],
"summary": {
"total": 4,
"succeeded": 2,
"failed": 2
}
}
}Response status is always 200 on partial success
The bulk endpoint returns 200 OK even if some rows fail — inspect results[].status and
summary to decide what to retry. A 400/422 is only returned when the whole request
is malformed (bad JSON, missing transfers, more than 100 items, etc.).
Bulk-only error codes
Rows can carry any of the common transfer errors above, plus:
error_code | When |
|---|---|
WALLET_TRANSFER_UNEXPECTED_ERROR | An unhandled server error. Retry with the same batch idempotency key. |
Best practices
- Always send
X-Idempotency-Key. Every write endpoint requires it; without one you get422 WALLET_IDEMPOTENCY_KEY_REQUIRED. Choose a stable value derived from your own business record (invoice id, payroll run id, etc.) so retries collapse cleanly. - Trust
balance_centimes, notbalance. The decimal string is for display only — never do arithmetic against it in your code. - For bulk, prefer larger batches over many singles. One 100-item batch is far cheaper than 100 individual calls and gets you a single consolidated report.
- Handle
WALLET_TRANSFER_DAILY_LIMITgracefully. If you hit it mid-batch, remaining rows will keep failing until the 24 h rolling window frees up.