Sama AlThawaf API
Reference documentation and live tester for the Sama AlThawaf payment & top-up platform.
Introduction
What the API is, how it is organised, and what is needed before getting started.
The Sama AlThawaf API lets clients buy prepaid products (pulsa, packages, token listrik) and pay bills on behalf of their customers. Every resource lives under the /api/v2 base path and speaks JSON over HTTPS.
| Item | Value |
|---|---|
| Base URL | https://<host>/api/v2 |
| Content Type | application/json |
| Authentication | Per-request headers, see Authentication |
| Response envelope | {"code": <int>, "status": <string>, "data": <...>} |
Resources
| Resource | Purpose |
|---|---|
| Account | Own account details and platform balance. |
| Products | Catalog of products available to the account. |
| Transactions | Top-up purchases, lookups, and historical reports. |
| Bills | Bill inquiry and bill payment (e.g. Telkomsel OMNI). |
Always send a unique transactionId for mutating calls (top-up, bill pay). The platform rejects duplicate IDs, so re-sending the same request cannot double-charge the customer.
Authentication
Every endpoint, except none. All API endpoints require credentials.
Authentication uses two headers on every request. A client receives a User ID and a secret API key when the account is provisioned. Keep the API key private; anyone holding it can use the account balance.
| Header | Description |
|---|---|
userid | The account’s user ID, e.g. thawaf or the client platform ID. |
api-key | Secret client key bound to the account. |
Example request headers
userid:api-key: Content-Type: application/json <!-- only for POST & PUT bodies -->
Requests without a valid key return 401 with {"code": 401, "status": "Error", "detail": "key tidak sesuai, silahkan hubungi cs"}. Missing headers fall through to the generic 404 error response.
Response format
Success, pending, and error responses all use the same envelope.
Every response is a JSON object with three keys:
| Key | Type | Description |
|---|---|---|
code | integer | Status code. Matches the HTTP status. |
status | string | Human status: Success, Pending, or Error. |
data | object | array | null | Payload. Omitted on plain errors, where detail is used instead. |
detail | string | Error message. Present only in error responses. |
Success
{
"code": 200,
"status": "Success",
"data": { }
}
Pending (mutating calls queued for processing)
{
"code": 201,
"status": "Pending",
"data": { }
}
Error
{
"code": 400,
"status": "Error",
"detail": "Produk tidak tersedia"
}
Status codes
Webhooks
How the platform notifies clients when an async transaction finishes.
Top-ups and bill payments run asynchronously. When a transaction settles, the platform POSTs the final transaction object to the callback URL configured on the account (stored as the client url.callback).
| Item | Value |
|---|---|
| Method | POST |
| Content Type | application/json |
| Body | A full transaction object (same shape as the data of a transaction response) |
| Timeout | The callback should accept connections on user.client.url.callback. |
Example callback payload
{
"transactionId": "TRX-20260907-0001",
"refId": "SAT2409070000001",
"date": "2026-09-07T08:30:00.000Z",
"product": {
"code": "TLKOM10",
"name": "Telkomsel 10.000",
"detail": "Paket isi ulang Telkomsel 10k",
"category": "Mobile",
"type": "Prepaid",
"price": 12000,
"status": "Active",
"operator": "Telkomsel"
},
"customerId": "081234567890",
"price": 12000,
"priceAdminFee": 0,
"priceDisclaimer": 0,
"priceSubTotal": 12000,
"action": "Top Up",
"payment": {
"status": "Success",
"method": { "id": "sat-deposit", "name": "Sama alThawaf Deposit", "disclaimer": 0 }
},
"sn": "7755112233",
"message": "Pembelian Telkomsel 10.000 Ke 081234567890 berhasil, SN = 7755112233",
"note": "",
"status": "Success",
"userId": "",
"user": {
"userId": "",
"name": "",
"email": "ops@example.com",
"status": "Active",
"validate": true,
"platform": { "key": "***", "balance": 975000 }
},
"code": 200
}
Submit a top-up → receive 201 Pending → poll GET /transaction/detail with the transactionId, or simply wait for the webhook. Reply to the webhook with HTTP 200 as fast as possible; retries are not guaranteed.