Area API

PostmanTest on Postman

Read-only lookup endpoints for Indonesian administrative regions (Province, City, District, Village).

Read-only lookup endpoints for Indonesian administrative regions: Province → City → District → Village. Typically used to populate cascading dropdowns (e.g. address forms, verification, shipping).

The list is hierarchical: pick a province to get its cities, pick a city to get its districts, and so on. Each endpoint returns the region code used as the parameter for the next level down.


Base URL

EnvironmentBase URL
Productionhttps://my.ipaymu.com/api
Sandboxhttps://sandbox.ipaymu.com/api

Adjust to your deployment. Locally this maps to {APP_URL}/api.

All Area endpoints are under the /areas prefix.


Authentication

Every request must be signed. The signature middleware (signaturecheck) requires these HTTP headers:

HeaderRequiredDescription
va✅ YesYour iPaymu Virtual Account number (the account's baccount).
signature✅ YesHMAC-SHA256 signature of the request (see below).
timestampRecommendedRequest timestamp, format YYYYMMDDHHmmss.
Content-Type✅ Yesapplication/json

The account tied to the va must be active and have an API key.

Signature generation

requestBody  = lowercase( sha256( <raw request body> ) )
stringToSign = <HTTP_METHOD> + ":" + <VA> + ":" + requestBody + ":" + <API_KEY>
signature    = hmac_sha256( stringToSign, <API_KEY> )

Notes:

  • <HTTP_METHOD> must be upper-case (GET).
  • For requests without a body (all Area GET endpoints), use the empty JSON object {} as the body when hashing — i.e. requestBody = sha256("{}").
  • <API_KEY> is used both as part of stringToSign and as the HMAC secret key.

Example Signature Code

const crypto = require('crypto');

const va = '0000001234567890';
const apiKey = 'YOUR_API_KEY';
const method = 'GET';

const body = '{}';
const requestBody = crypto.createHash('sha256').update(body).digest('hex').toLowerCase();
const stringToSign = `${method}:${va}:${requestBody}:${apiKey}`;
const signature = crypto.createHmac('sha256', apiKey).update(stringToSign).digest('hex');
const timestamp = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);

Response envelope

All responses share the same wrapper:

{
  "Status": 200,
  "Success": true,
  "Message": "Success",
  "Data": []
}
FieldTypeDescription
StatusintegerHTTP status code, mirrored in the body.
Successbooleantrue on success, false on error.
MessagestringHuman-readable message.
Dataarray | nullResult payload (an array of region objects).

Endpoints

1. Get Provinces

Returns all provinces, ordered by name (A→Z).

GET /areas/province

Parameters: none

Example request

curl --location 'https://my.ipaymu.com/api/areas/province' \
  --header 'Content-Type: application/json' \
  --header 'va: 0000001234567890' \
  --header 'signature: <generated-signature>' \
  --header 'timestamp: 20260629103000'

Example response 200 OK

{
  "Status": 200,
  "Success": true,
  "Message": "Success",
  "Data": [
    { "code": "11", "name": "ACEH" },
    { "code": "51", "name": "BALI" },
    { "code": "36", "name": "BANTEN" }
  ]
}

2. Get Cities by Province

Returns the cities/regencies belonging to a province.

GET /areas/city/{province}

Path parameters

ParamTypeDescription
provincestringProvince code (from the Provinces endpoint).

Example request

curl --location 'https://my.ipaymu.com/api/areas/city/51' \
  --header 'Content-Type: application/json' \
  --header 'va: 0000001234567890' \
  --header 'signature: <generated-signature>' \
  --header 'timestamp: 20260629103000'

Example response 200 OK

{
  "Status": 200,
  "Success": true,
  "Message": "Success",
  "Data": [
    { "code": "5171", "name": "KOTA DENPASAR" },
    { "code": "5103", "name": "KABUPATEN BADUNG" }
  ]
}

3. Get Districts by City

Returns the districts (kecamatan) belonging to a city.

GET /areas/district/{city}

Path parameters

ParamTypeDescription
citystringCity code (from the Cities endpoint).

Example request

curl --location 'https://my.ipaymu.com/api/areas/district/5171' \
  --header 'Content-Type: application/json' \
  --header 'va: 0000001234567890' \
  --header 'signature: <generated-signature>' \
  --header 'timestamp: 20260629103000'

Example response 200 OK

{
  "Status": 200,
  "Success": true,
  "Message": "Success",
  "Data": [
    { "code": "517101", "name": "DENPASAR SELATAN" },
    { "code": "517102", "name": "DENPASAR TIMUR" }
  ]
}

4. Get Villages by District

Returns the villages (kelurahan/desa) belonging to a district. Village objects also include a postal code.

GET /areas/village/{district}

Path parameters

ParamTypeDescription
districtstringDistrict code (from the Districts endpoint).

Example request

curl --location 'https://my.ipaymu.com/api/areas/village/517101' \
  --header 'Content-Type: application/json' \
  --header 'va: 0000001234567890' \
  --header 'signature: <generated-signature>' \
  --header 'timestamp: 20260629103000'

Example response 200 OK

{
  "Status": 200,
  "Success": true,
  "Message": "Success",
  "Data": [
    { "code": "5171011001", "name": "SANUR", "postal_code": "80227" },
    { "code": "5171011002", "name": "RENON", "postal_code": "80226" }
  ]
}

Field reference

Province / City / District

FieldTypeDescription
codestringRegion code. Use it as the path parameter for the next level.
namestringRegion name.

Village (same as above, plus)

FieldTypeDescription
postal_codestring | nullPostal code (null if unavailable).

Error responses

On authentication failure the request is rejected before reaching the controller:

StatusMessageCause
401invalid header param - va is requiredMissing va header.
401invalid header param - signature is requiredMissing signature header.
401unauthorized credentialva not found, inactive account, or no API key.
401unauthorized signatureSignature does not match.

Example error

{
  "Status": 401,
  "Success": false,
  "Message": "unauthorized signature",
  "Data": null
}

A valid request for a code that has no children (or an unknown code) returns 200 with an empty Data array ([]).


Notes

  • Method: Documented as GET. The routes accept any HTTP method, but use GET consistently so it matches the method used in your signature.
  • Caching: Region data is static; cache responses on the client to reduce calls.
  • Ordering: Provinces are returned A→Z by name. Cities, districts, and villages are returned in the database's natural order.

On this page