MENU navbar-image

KEMENPAR Tourism Business Verification API

This API provides verification services for Business Identification Numbers (NIB) and KBLI codes within the tourism sector through integration with the Online Single Submission (OSS) system.


System Architecture

OTA Platform → KEMENPAR API → OSS Validation System

OTA platforms send a POST request to the KEMENPAR API to validate whether a submitted NIB and its associated KBLI (Indonesian Standard Industrial Classification) are registered and compliant within the OSS system.


Security Features

The API implements multiple security layers to ensure secure communication, request authenticity, and protection against abuse.

Supported Security Mechanisms


Authentication

The KEMENPAR API uses HMAC SHA256 signature-based authentication to validate all incoming requests securely.

All OTA partners and third-party systems must generate a valid request signature before accessing the API endpoint.

Each authorized OTA developer team will receive the following credentials from KEMENPAR:

These credentials are required for all API requests.


Signature Generation

The X-signature header must be generated using the provided client_secret.

Signature Formula

stringToSign = X-cons-id + "&" + X-timestamp

Example

SBX_AIRBNB_7RT4D&1779075264

Base URL

Sandbox Environment

https://api-industri.kemenpar.tech

Production Environment

https://api-industri.kemenpar.go.id

Endpoint Format

Sandbox

/api/{ota}/sandbox/v1/verify-nib

Production

/api/{ota}/v1/verify-nib

Supported OTA Values

OTA Platform OTA Code
Airbnb airbnb
Agoda agoda
Booking.com booking
Expedia expedia
OYO oyo
RedDoorz reddoorz
Traveloka traveloka
Tiket.com tiket
Trip.com trip

Authentication Flow

Every API request MUST follow the steps below:

  1. Retrieve API credentials provided by KEMENPAR:

    • X-cons-id
    • X-user-key
    • client_secret
  2. Generate a UTC Unix Timestamp.

  3. Create the signature string:

Note: All timestamps MUST use UTC timezone.

cons_id&timestamp

Example:

SBX_AIRBNB_7RT4D&1779075264
  1. Generate an HMAC SHA256 hash using client_secret.

  2. Encode the generated hash using Base64.

  3. Send all required headers in the request.


Signature Specification

All API requests must include a valid digital signature for authentication and request validation.

The signature is generated using the Consumer ID (X-cons-id) combined with the request timestamp (X-timestamp). The generated value must be sent in the X-signature request header.

The signature ensures that every request comes from an authorized client and prevents unauthorized API access.

Signature String

cons_id&timestamp

Hash Algorithm

HMAC SHA256

Encoding

Base64

Signature Generation

The X-signature header must be generated using the provided client_secret.

Signature Formula

stringToSign = X-cons-id + "&" + X-timestamp

Postman Environment Setup

Create a Postman Environment with the following variables:

Variable Description
client_secret Secret key used for signature generation

Required Headers

Create a Postman Headers with the following variables:

Header Required Description
Content-Type YES Request content type (application/json)
X-cons-id YES Consumer ID provided by KEMENPAR
X-user-key YES API user key provided by KEMENPAR

cURL Example (Sandbox)

Use the following cURL command to test the API or import it directly into Postman.

curl --location 'https://api-industri.kemenpar.tech/api/airbnb/sandbox/v1/verify-nib' \
--header 'Content-Type: application/json' \
--header 'X-cons-id: SBX_BOOKING_9XQ2L' \
--header 'X-user-key: sbxBk9Qf71mNxL0vUtW2cD8aPqYeRjHsF6AiKzMp' \
--data '{ 
    "nib": "9120205342672", 
    "kbli": "55110", 
    "nku": "202112011258206434761" 
}'

Sample Request Body

{
  "nib": "3012210012345",
  "kbli": "55110",
  "nku": "202112051258206434567"
}

cURL Example (Production)

Use the following cURL command to the API or import it directly into Postman.

curl --location 'https://api-industri.kemenpar.go.id/api/airbnb/v1/verify-nib' \
--header 'Content-Type: application/json' \
--header 'X-cons-id: SBX_BOOKING_9XQ2L' \
--header 'X-user-key: sbxBk9Qf71mNxL0vUtW2cD8aPqYeRjHsF6AiKzMp' \
--data '{ 
    "nib": "9120205342672", 
    "kbli": "55110", 
    "nku": "202112011258206434761" 
}'

Sample Request Body

{
  "nib": "3012210012345",
  "kbli": "55110",
  "nku": "202112051258206434567"
}

Postman Pre-request Script

Paste the following script into the Postman Pre-request Script tab.

// ===== LOAD HEADER & ENVIRONMENT VARIABLES =====
const cons_id = pm.request.headers.get("X-cons-id");
const user_key = pm.request.headers.get("X-user-key");
const secret = pm.environment.get("client_secret");
const timestamp = Math.floor(Date.now() / 1000);

// ===== SIGNATURE =====
const stringToSign = cons_id + "&" + timestamp;
const hash = CryptoJS.HmacSHA256(stringToSign, secret);
const signature = CryptoJS.enc.Base64.stringify(hash);

// ===== SET to ENV =====
pm.environment.set("X-timestamp", timestamp);
pm.environment.set("X-signature", signature);

// ===== SET HEADER OTOMATIS (INI PENTING 🔥) =====
pm.request.headers.upsert({ key: "X-cons-id", value: cons_id });
pm.request.headers.upsert({ key: "X-user-key", value: user_key });
pm.request.headers.upsert({ key: "X-timestamp", value: timestamp.toString() });
pm.request.headers.upsert({ key: "X-signature", value: signature });
pm.request.headers.upsert({ key: "Content-Type", value: "application/json" });

// DEBUG
console.log("X-timestamp:", timestamp);
console.log("X-signature:", signature);

Example Success Response

{
  "code": 200,
  "message": "Success",
  "status": true,
  "data": {
    "nib": "3012210013123",
    "kbli": "55110",
    "nku": "R-202112051258206434567"
  }
}

Example Error Response

{
  "code": 401,
  "message": "Invalid Signature",
  "status": false
}

Multi-language Signature Script

PHP


<?php

$consId = 'YOUR_CONS_ID';
$clientSecret = 'YOUR_CLIENT_SECRET';
$userKey = 'YOUR_USER_KEY';


// Generate UNIX UTC timestamp
$timestamp = (string) time();


// Create signature string
$stringToSign = $consId . '&' . $timestamp;


// Generate HMAC SHA256 + Base64 signature
$signature = base64_encode(
    hash_hmac(
        'sha256',
        $stringToSign,
        $clientSecret,
        true
    )
);


// Request headers
$headers = [
    'Content-Type: application/json',
    'X-cons-id: ' . $consId,
    'X-user-key: ' . $userKey,
    'X-timestamp: ' . $timestamp,
    'X-signature: ' . $signature,
];


print_r($headers);

JavaScript

const crypto = require('crypto');

const consId = 'YOUR_CONS_ID';
const clientSecret = 'YOUR_CLIENT_SECRET';
const userKey = 'YOUR_USER_KEY';


// Generate UNIX UTC timestamp
const timestamp = Math.floor(Date.now() / 1000).toString();


// Create signature string
const stringToSign = consId + '&' + timestamp;


// Generate HMAC SHA256 + Base64 signature
const signature = crypto
    .createHmac(
        'sha256',
        clientSecret
    )
    .update(stringToSign)
    .digest('base64');


// Request headers
const headers = {
    'Content-Type': 'application/json',
    'X-cons-id': consId,
    'X-user-key': userKey,
    'X-timestamp': timestamp,
    'X-signature': signature
};


console.log(headers);

Python

import time
import hmac
import hashlib
import base64


cons_id = "YOUR_CONS_ID"
client_secret = "YOUR_CLIENT_SECRET"
user_key = "YOUR_USER_KEY"


# Generate UNIX UTC timestamp
timestamp = str(
    int(time.time())
)


# Create signature string
string_to_sign = (
    cons_id +
    "&" +
    timestamp
)


# Generate HMAC SHA256 + Base64 signature
signature = base64.b64encode(
    hmac.new(
        client_secret.encode("utf-8"),
        string_to_sign.encode("utf-8"),
        hashlib.sha256
    ).digest()
).decode("utf-8")


# Request headers
headers = {
    "Content-Type": "application/json",
    "X-cons-id": cons_id,
    "X-user-key": user_key,
    "X-timestamp": timestamp,
    "X-signature": signature
}


print(headers)

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class SignatureGenerator {

    public static void main(String[] args)
            throws Exception {

        String consId = "FILL_CONS_ID";
        String secretKey = "FILL_SECRET";
        String userKey = "FILL_USER_KEY";

        // Generate Unix timestamp (UTC)
        String timestamp = String.valueOf(
            System.currentTimeMillis() / 1000
        );

        // Create signature string
        String stringToSign =
            consId + "&" + timestamp;

        // Generate HMAC SHA256
        Mac sha256Hmac = Mac.getInstance(
            "HmacSHA256"
        );

        SecretKeySpec secretKeySpec =
            new SecretKeySpec(
                secretKey.getBytes(),
                "HmacSHA256"
            );

        sha256Hmac.init(secretKeySpec);

        // Generate Base64 Signature
        String signature =
            Base64.getEncoder().encodeToString(
                sha256Hmac.doFinal(
                    stringToSign.getBytes()
                )
            );

        // Request Headers
        Map<String, String> headers =
            new HashMap<>();

        headers.put("X-cons-id", consId);
        headers.put("X-user-key", userKey);
        headers.put("X-timestamp", timestamp);
        headers.put("X-signature", signature);
        headers.put("Content-Type", "application/json");

        System.out.println(headers);
    }
}

C

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static void Main()
    {
        string consId = "FILL_CONS_ID";
        string secretKey = "FILL_SECRET";
        string userKey = "FILL_USER_KEY";

        // Generate Unix timestamp (UTC)
        string timestamp =
            DateTimeOffset.UtcNow
            .ToUnixTimeSeconds()
            .ToString();

        // Create signature string
        string stringToSign =
            consId + "&" + timestamp;

        // Generate HMAC SHA256 signature
        string signature;

        using (
            HMACSHA256 hmac =
            new HMACSHA256(
                Encoding.UTF8.GetBytes(secretKey)
            )
        )
        {
            byte[] hash =
                hmac.ComputeHash(
                    Encoding.UTF8.GetBytes(stringToSign)
                );

            signature =
                Convert.ToBase64String(hash);
        }

        // Request headers
        var headers =
            new Dictionary<string, string>
        {
            { "X-cons-id", consId },
            { "X-user-key", userKey },
            { "X-timestamp", timestamp },
            { "X-signature", signature },
            { "Content-Type", "application/json" }
        };

        // Print headers
        foreach (var header in headers)
        {
            Console.WriteLine(
                $"{header.Key}: {header.Value}"
            );
        }
    }
}

HTTP Response Codes

Condition HTTP Code Status Message
NIB, KBLI, and NKU match successfully 200 true Success
NIB not found 200 false NIB not found
KBLI does not match 200 false KBLI does not match
NKU does not match 200 false NKU does not match
Missing required fields 422 false Validation Error
Invalid JSON format 400 false Bad Request
Invalid signature or authentication failed 401 false Unauthorized / Invalid Signature
IP address is not whitelisted 403 false IP Address Not Allowed
Invalid API endpoint URL 404 false Endpoint Not Found
Request limit exceeded 429 false Too Many Requests
Server, database, or OSS service unavailable 500 false Internal Server Error

Security Notes


IP Whitelisting (Production Only)

Production API access is protected using IP whitelisting.

All OTA partners must register their static public IP addresses before accessing the Production environment.

Requests originating from unregistered IP addresses will be rejected automatically.

Requirements

Important Notes

Example Response

{
    "success": {
        "code": 200,
        "message": "Success",
        "status": true
    },

    "nib_not_found": {
        "code": 200,
        "message": "NIB not found",
        "status": false
    },

    "kbli_not_match": {
        "code": 200,
        "message": "KBLI does not match",
        "status": false
    },

    "nku_not_match": {
        "code": 200,
        "message": "NKU does not match",
        "status": false
    },

    "validation_error": {
        "code": 422,
        "message": "Validation Error",
        "status": false
    },

    "bad_request": {
        "code": 400,
        "message": "Bad Request",
        "status": false
    },

    "invalid_signature": {
        "code": 401,
        "message": "Unauthorized / Invalid Signature",
        "status": false
    },

    "ip_not_allowed": {
        "code": 403,
        "message": "IP Address Not Allowed",
        "status": false
    },

    "endpoint_not_found": {
        "code": 404,
        "message": "Endpoint Not Found",
        "status": false
    },

    "too_many_requests": {
        "code": 429,
        "message": "Too Many Requests",
        "status": false
    },

    "internal_server_error": {
        "code": 500,
        "message": "Internal Server Error",
        "status": false
    }
}

Endpoints

POST api/{ota}/sandbox/v1/verify-nib

Example request:
curl --request POST \
    "http://localhost/api/consequatur/sandbox/v1/verify-nib" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nib\": 11613.31890586,
    \"kbli\": 11613.31890586,
    \"nku\": \"consequatur\"
}"
const url = new URL(
    "http://localhost/api/consequatur/sandbox/v1/verify-nib"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "nib": 11613.31890586,
    "kbli": 11613.31890586,
    "nku": "consequatur"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request   

POST api/{ota}/sandbox/v1/verify-nib

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ota   string     

Example: consequatur

Body Parameters

nib   number     

Example: 11613.31890586

kbli   number     

Example: 11613.31890586

nku   string     

Example: consequatur

POST api/{ota}/v1/verify-nib

Example request:
curl --request POST \
    "http://localhost/api/consequatur/v1/verify-nib" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nib\": \"consequatur\",
    \"kbli\": \"consequatur\",
    \"id_proyek\": \"consequatur\"
}"
const url = new URL(
    "http://localhost/api/consequatur/v1/verify-nib"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "nib": "consequatur",
    "kbli": "consequatur",
    "id_proyek": "consequatur"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request   

POST api/{ota}/v1/verify-nib

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ota   string     

Example: consequatur

Body Parameters

nib   string     

Example: consequatur

kbli   string     

Example: consequatur

id_proyek   string     

Example: consequatur