Skip to main content

Payout Introduction

This document specifies the protocols between the Airpay Platform and merchant systems. Intended for merchants who want to integrate Payout transactions through a Single API — supporting PPOB FIXED denomination, Open Denomination bank transfers, and Bill Payment (Postpaid) — all through one unified endpoint.

3
Product Types
8
Endpoints
REST
Protocol

Supported vendors:

Product Type Examples
PPOB (FIXED / BILL) Mobile Credit, PLN Token, Data Package, PLN, BPJS, PDAM, etc.
OPEN (Bank Transfer) BCA, BNI, BRI, Mandiri, BNC, etc.

Environment

Production and Sandbox use the same path prefix — differentiated by domain and API key environment setting.

Environment Base URL API Key Type
Production https://api.airpay.mobi env = 'production'
Sandbox https://stagingapi.airpay.mobi env = 'development'

All Payout endpoints are served under the /api/v2/ext prefix on both environments.

Sample URLs:

POST https://api.airpay.mobi/api/v2/ext/payout/order
GET  https://api.airpay.mobi/api/v2/ext/payout/status

POST https://stagingapi.airpay.mobi/api/v2/ext/payout/order
GET  https://stagingapi.airpay.mobi/api/v2/ext/payout/status

Authentication

Before calling any Payout API, merchants must obtain an Access Token. The client_id and client_secret are provided during merchant onboarding.

✅ Auth Flow

1. POST /api/v2/ext/auth/token with Basic Auth (client_id:client_secret) → access_token
2. Add Authorization: Bearer <token> + apikey: <your_api_key> on every payout request.

ℹ️ Token Expiry

Tokens are valid for 15 minutes. Repeated requests within the same window return the same token (deterministic).

Get Access Token

Authenticate with Basic Auth to receive a 15-minute Bearer token.

HEADERS (BASIC AUTH):

Name Type Required Description
Authorization string Required Basic base64(client_id:client_secret)
Authorizations:
PayoutBasicAuth

Responses

Request samples

curl --request POST \
  --url https://api.airpay.mobi/api/v2/ext/auth/token \
  --header 'Authorization: Basic <base64(id:secret)>'

Response samples

Content type
application/json
{
  • "code": "200",
  • "message": "Access Token Generated",
  • "data": {
    }
}

Balance Inquiry

Check merchant's payin and payout wallet balance. Both Authorization: Bearer <token> and apikey header are required.

Authorizations:
(PayoutBearerAuthPayoutApiKeyAuth)
header Parameters
Authorization
required
string
Example: Bearer <access_token>

Bearer access token obtained from the Get Access Token endpoint

apikey
required
string

Merchant API key

Responses

Request samples

curl https://api.airpay.mobi/api/v2/ext/balance-inquiry \
  -H 'Authorization: Bearer <token>' \
  -H 'apikey: <your_api_key>'

Response samples

Content type
application/json
{
  • "code": "200",
  • "message": "Success",
  • "data": {
    }
}

Service API Specs

Unified endpoint for FIXED, OPEN denomination, and BILL payment. Routes automatically based on product type.

Product Type Routing

Type Flow
FIXED 1 step — direct pay
OPEN / BILL flag=INQ → inquiry_id → flag=PAY

API Payout Order

Unified endpoint for FIXED, OPEN denomination, and BILL payment. Routes automatically based on product type. Merchant must obtain an Access Token first and pass it via Authorization: Bearer <token>.

Authorizations:
(PayoutBearerAuthPayoutApiKeyAuth)
header Parameters
Authorization
required
string
Example: Bearer <access_token>

Bearer access token obtained from the Get Access Token endpoint

apikey
required
string

Merchant API key

Request Body schema: application/json
flag
string
Enum: "INQ" "PAY"

Step control for OPEN/BILL. Default PAY. FIXED ignores this.

product_code
required
string

Master product code e.g. TSEL-5000, PAYOUT_BCA, PLN-PASCA

customer_number
required
string

Phone number, bank account number, or customer ID

reference_id
required
string

Unique merchant-generated transaction ID

amount
number

Required for OPEN products. Transfer nominal in IDR (min 10,000)

inquiry_id
string

Required for OPEN/BILL flag=PAY. From preceding INQ step. Expires in 15 min.

callback_url
string

URL for async webhook when transaction completes or fails

Responses

Request samples

Content type
application/json
Example
{
  • "product_code": "TSEL-5000",
  • "customer_number": "081234567890",
  • "reference_id": "REF-FIXED-001",
  • "callback_url": "https://yourapp.com/webhook"
}

Response samples

Content type
application/json
Example
{
  • "code": "200",
  • "message": "Order Created Successfully",
  • "data": {
    }
}

Check Transaction Status

Query transaction status by transaction_id or reference_id. Also available as GET with the same fields sent as query params.

Authorizations:
(PayoutBearerAuthPayoutApiKeyAuth)
header Parameters
Authorization
required
string
Example: Bearer <access_token>

Bearer access token obtained from the Get Access Token endpoint

apikey
required
string

Merchant API key

Request Body schema: application/json
transaction_id
string

Airpay-generated transaction ID

reference_id
string

Merchant-generated reference ID

Responses

Request samples

Content type
application/json
{
  • "reference_id": "REF-FIXED-001"
}

Response samples

Content type
application/json
Example
{
  • "code": "200",
  • "message": "Transaction Status Retrieved",
  • "data": {
    }
}

Merchant Integration Requirements

Postback URL: Reply URL at merchant server for receiving postback notifications from Airpay when transaction status changes.

  • Each merchant must provide only one postback URL per environment.
  • The endpoint must respond with 200 OK to confirm receipt.
  • Airpay will retry failed callbacks up to 5 times with exponential backoff.
⚠️ Auto-Refund on FAILED

If a transaction is FAILED, the merchant wallet is automatically refunded. Callback will still fire with status: "FAILED".

Callback

Whenever a transaction's status changes, Airpay will notify the merchant by sending an HTTP POST request with a JSON body to the postback URL registered by the merchant (see Merchant Integration Requirements above).

The merchant's endpoint is expected to respond with 200 OK once the callback has been received successfully.

Callback Notification

This is not an Airpay endpoint — it is the URL hosted by the merchant. Airpay calls it to notify of payment status changes. Must respond with 200 OK.

Request Body schema: application/json
transaction_id
required
string

Airpay transaction ID

reference_id
required
string

Merchant reference ID

product_code
required
string

Product code

customer_number
required
string

Customer phone / account number

price
required
number

Selling price in IDR (Merchant Billing)

status
required
string
Enum: "SUCCESS" "FAILED"
serial_number
string

Vendor SN (SUCCESS) or empty string (FAILED)

remarks
string

Description or failure reason (Auto-Refunded on FAILED)

processed_at
required
string

ISO 8601 / RFC 3339 timestamp

additional_data
object

Dynamic metadata object per transaction type

Responses

Request samples

Content type
application/json
Example
{
  • "transaction_id": "TRX-REF-FIXED-001-1724505000000",
  • "reference_id": "REF-FIXED-001",
  • "product_code": "TSEL-5000",
  • "customer_number": "081234567890",
  • "price": 5500,
  • "status": "SUCCESS",
  • "serial_number": "SN-DGF-12345678",
  • "remarks": "",
  • "processed_at": "2026-08-25T01:53:15+07:00",
  • "additional_data": { }
}

Sandbox

Sandbox uses the exact same endpoints as Production (https://stagingapi.airpay.mobi/api/v2/ext/...) — there is no separate path. An API key with env = 'development' is what routes a request to sandbox behavior. No wallet is deducted and no real vendor is hit.

✓ Sandbox Does ✗ Does NOT
Same request format as prod Deduct wallet balance
Returns PROCESS immediately Hit real vendor APIs
Simulates SUCCESS after ~2-3s Process real money
Sends a real webhook callback Mix with prod data
Isolates to a separate dev table Auto-refund (no deduction)

Example webhook payload sent to callback_url ~2-3s later (see Callback Notification for the field reference):

{
  "transaction_id":  "TRX-DEV-DEV-REF-001-...",
  "reference_id":    "DEV-REF-001",
  "product_code":    "TSEL-5000",
  "customer_number": "081234567890",
  "price":           5500,
  "status":          "SUCCESS",
  "serial_number":   "SN-DEV-1724505002500",
  "remarks":         "",
  "processed_at":    "2026-08-25T01:53:15+07:00",
  "additional_data": { "note": "[SANDBOX] Simulated callback notification." }
}

Reference

Response Codes

Standard 3-digit response codes across all Airpay Payout API endpoints.

Code HTTP Status Status Text Description
200 200 OK Request succeeded. Used for Order Create (status: PROCESS), Account Inquiry, Bill Inquiry, and Check Status.
400 400 Bad Request apikey header is empty, JSON payload is invalid, a required parameter is missing (product_code, customer_number, reference_id), payout wallet balance is insufficient, or inquiry_id is invalid/expired.
401 401 Unauthorized API key is invalid, expired, or not registered in the system.
403 403 Forbidden API key is for the wrong environment (e.g. a production API key used on a sandbox endpoint) or the Inquiry ID does not belong to the related merchant account.
404 404 Not Found Product not found / vendor not yet active, Inquiry ID data not found, or Transaction data not found on check status.
422 422 Unprocessable Entity Account inquiry failed at the destination bank/e-wallet, Bill inquiry failed at the biller, or the bill is already paid (Bill already paid).
500 500 Internal Server Error An internal server or database connection failure occurred.

Product Types

Three product types are supported, each with a different flow and request structure.

Type Flag Steps Example Products
FIXED PAY (or none) 1 step Pulsa, Token PLN, Paket Data, Voucher Game
OPEN INQ → PAY 2 steps PAYOUT_BCA, PAYOUT_BNI, PAYOUT_BRI, PAYOUT_MANDIRI
BILL INQ → PAY 2 steps PLN-PASCA, BPJS, PDAM, Telkom