TELEKOS.IDBack to Home
API REFERENCEv1RESTJSON

Build SMS verification into your app.

Search live stock, buy a number, and receive OTP messages with one predictable API.

GET CONNECTED

Start with your API key

All requests use HTTPS and return JSON.

Base URLhttps://otp.telekos.id/v1
HeaderAuthorization: Bearer YOUR_RANDOM_API_KEY

Keep your API key on the server. Never expose it in browser or mobile application code.

QUICK START

From catalog to OTP

  1. 01
    ApplicationsGET /apps
  2. 02
    CountriesGET /countries
  3. 03
    Live priceGET /prices
  4. 04
    Buy numberPOST /orders
  5. 05
    Receive OTPPoll or webhook
REFERENCE

Endpoints

ACCOUNT

Balance

Read the wallet balance shared by the dashboard and public API.

GET/balance

Get balance

Returns the current available balance in Indonesian rupiah.

Bearer auth
REQUEST · cURL
curl https://otp.telekos.id/v1/balance \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": {
    "balance": 17300,
    "currency": "IDR",
    "updated_at": "2026-10-08T10:05:22.000Z"
  }
}
CATALOG

Find a number

Choose an application, country, and live price before creating an order.

GET/apps

List applications

Returns the services that can receive verification messages.

Bearer auth
REQUEST · cURL
curl https://otp.telekos.id/v1/apps \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": [
    {
      "id": "wa",
      "name": "WhatsApp"
    },
    {
      "id": "tg",
      "name": "Telegram"
    }
  ]
}
  • Use the application id as the app query value on the next request.
GET/countries

List countries

Returns countries and their current aggregate stock for one application.

Bearer auth

Parameters

NameInTypeDescription
apprequiredquerystringApplication id from GET /apps.
REQUEST · cURL
curl "https://otp.telekos.id/v1/countries?app=wa" \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": [
    {
      "id": 6,
      "name": "Indonesia",
      "iso": "ID",
      "flag": "🇮🇩",
      "calling_code": "+62",
      "stock": 3455
    }
  ]
}
GET/prices

Get live prices

Returns the available price tiers, sorted from the cheapest offer.

Bearer authLive catalog

Parameters

NameInTypeDescription
apprequiredquerystringApplication id from GET /apps.
countryrequiredqueryintegerCountry id from GET /countries.
refreshquery0 | 1Set to 1 to request a fresh catalog lookup.
REQUEST · cURL
curl "https://otp.telekos.id/v1/prices?app=wa&country=6" \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": [
    {
      "id": "4zTr5nQ2xM8vK7pL",
      "offer_id": "opaque-signed-offer-token",
      "price": 2700,
      "currency": "IDR",
      "stock": 2736
    }
  ],
  "meta": {
    "quoted_at": "2026-10-08T10:06:00.000Z"
  }
}
  • Send offer_id exactly as returned when you create the order.
  • Offers are short-lived. If OFFER_EXPIRED is returned, fetch /prices again.
ORDERS

Buy and manage numbers

Create an order, poll for OTP messages, then finish or cancel it.

GET/orders

List active orders

Returns active API and dashboard orders for the account, five per page.

Bearer auth

Parameters

NameInTypeDescription
pagequeryintegerPage number. Defaults to 1.
REQUEST · cURL
curl "https://otp.telekos.id/v1/orders?page=1" \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": [
    {
      "id": "TRX-A1B2C3D4E5F6",
      "status": "waiting",
      "number": "+628123456789",
      "app": {
        "id": "wa",
        "name": "WhatsApp"
      },
      "country": {
        "id": 6,
        "name": "Indonesia",
        "iso": "ID",
        "calling_code": "+62"
      },
      "price": 2700,
      "currency": "IDR",
      "otps": [],
      "can_cancel": false,
      "cancel_available_at": "2026-10-08T10:09:00.000Z",
      "created_at": "2026-10-08T10:06:00.000Z",
      "expires_at": "2026-10-08T10:26:00.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "pages": 1,
    "active": 1,
    "total": 12
  }
}
  • Orders created through the public API also appear in the TELEKOS.ID dashboard.
POST/orders

Create an order

Purchases one virtual number using a current offer from GET /prices.

Bearer authIdempotency required

Parameters

NameInTypeDescription
Idempotency-KeyrequiredheaderstringUnique 8–128 character request key.
offer_idrequiredJSON bodystringOpaque offer token returned by GET /prices.
REQUEST · cURL
curl -X POST https://otp.telekos.id/v1/orders \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY" \
  -H "Idempotency-Key: checkout-20261008-001" \
  -H "Content-Type: application/json" \
  -d '{"offer_id":"opaque-signed-offer-token"}'
RESPONSE · 201 Created
{
  "data": {
    "id": "TRX-A1B2C3D4E5F6",
    "status": "waiting",
    "number": "+628123456789",
    "app": {
      "id": "wa",
      "name": "WhatsApp"
    },
    "country": {
      "id": 6,
      "name": "Indonesia",
      "iso": "ID",
      "calling_code": "+62"
    },
    "price": 2700,
    "currency": "IDR",
    "otps": [],
    "can_cancel": false,
    "cancel_available_at": "2026-10-08T10:09:00.000Z",
    "created_at": "2026-10-08T10:06:00.000Z",
    "expires_at": "2026-10-08T10:26:00.000Z"
  },
  "meta": {
    "balance": 17300,
    "currency": "IDR"
  }
}
  • Safely retry the exact same purchase with the same Idempotency-Key.
  • Never create a new key while a PURCHASE_CONFIRMATION_PENDING order is being confirmed.
GET/orders/{order_id}

Retrieve an order

Poll this endpoint to read the latest status and every OTP message received.

Bearer auth

Parameters

NameInTypeDescription
order_idrequiredpathstringPublic transaction id, for example TRX-A1B2C3D4E5F6.
REQUEST · cURL
curl https://otp.telekos.id/v1/orders/TRX-A1B2C3D4E5F6 \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": {
    "id": "TRX-A1B2C3D4E5F6",
    "status": "received",
    "number": "+628123456789",
    "app": {
      "id": "wa",
      "name": "WhatsApp"
    },
    "country": {
      "id": 6,
      "name": "Indonesia",
      "iso": "ID",
      "calling_code": "+62"
    },
    "price": 2700,
    "currency": "IDR",
    "otps": [
      {
        "id": "OTP-C2D8F9A1",
        "code": "739733",
        "message": "Your WhatsApp code is 739733",
        "sender": "WhatsApp",
        "received_at": "2026-10-08T10:07:31.000Z"
      }
    ],
    "can_cancel": false,
    "cancel_available_at": "2026-10-08T10:09:00.000Z",
    "created_at": "2026-10-08T10:06:00.000Z",
    "expires_at": "2026-10-08T10:26:00.000Z"
  }
}
  • Use the newest item in otps when more than one verification message is received.
POST/orders/{order_id}/cancel

Cancel an order

Cancels an eligible order and returns the updated wallet balance.

Bearer authState mutation

Parameters

NameInTypeDescription
order_idrequiredpathstringThe order id you want to cancel.
REQUEST · cURL
curl -X POST https://otp.telekos.id/v1/orders/TRX-A1B2C3D4E5F6/cancel \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": {
    "id": "TRX-A1B2C3D4E5F6",
    "status": "cancelled",
    "number": null,
    "app": {
      "id": "wa",
      "name": "WhatsApp"
    },
    "country": {
      "id": 6,
      "name": "Indonesia",
      "iso": "ID",
      "calling_code": "+62"
    },
    "price": 2700,
    "currency": "IDR",
    "otps": [],
    "can_cancel": false,
    "cancel_available_at": "2026-10-08T10:09:00.000Z",
    "created_at": "2026-10-08T10:06:00.000Z",
    "expires_at": "2026-10-08T10:26:00.000Z"
  },
  "meta": {
    "balance": 20000,
    "currency": "IDR"
  }
}
  • Only call this endpoint when can_cancel is true and no OTP has been received.
POST/orders/{order_id}/finish

Finish an order

Marks an order completed after its verification message has been received.

Bearer authState mutation

Parameters

NameInTypeDescription
order_idrequiredpathstringThe received order you want to complete.
REQUEST · cURL
curl -X POST https://otp.telekos.id/v1/orders/TRX-A1B2C3D4E5F6/finish \
  -H "Authorization: Bearer YOUR_RANDOM_API_KEY"
RESPONSE · 200 OK
{
  "data": {
    "id": "TRX-A1B2C3D4E5F6",
    "status": "completed",
    "number": "+628123456789",
    "app": {
      "id": "wa",
      "name": "WhatsApp"
    },
    "country": {
      "id": 6,
      "name": "Indonesia",
      "iso": "ID",
      "calling_code": "+62"
    },
    "price": 2700,
    "currency": "IDR",
    "otps": [
      {
        "id": "OTP-C2D8F9A1",
        "code": "739733",
        "message": "Your WhatsApp code is 739733",
        "sender": "WhatsApp",
        "received_at": "2026-10-08T10:07:31.000Z"
      }
    ],
    "can_cancel": false,
    "cancel_available_at": "2026-10-08T10:09:00.000Z",
    "created_at": "2026-10-08T10:06:00.000Z",
    "expires_at": "2026-10-08T10:26:00.000Z"
  }
}
  • Finishing an already completed order is safe and returns the completed order.
WEBHOOKS

Receive events without polling

Configure your HTTPS destination and signing secret in Profile.

order.otporder.completedorder.cancelledorder.expired
Signature headerX-Telekos-Signature
Signed contenttimestamp.raw_body
Delivery idX-Telekos-Delivery
Expected response2xx

The signature format is t=unix_timestamp,v1=hmac_sha256_hex. Verify the HMAC-SHA256 signature against the unmodified raw request body, and use the delivery id to ignore duplicates.

EVENT · order.otp
{
  "id": "EVT-7E0A9D31",
  "type": "order.otp",
  "created_at": "2026-10-08T10:07:31.000Z",
  "data": {
    "order": {
      "id": "TRX-A1B2C3D4E5F6",
      "status": "received",
      "number": "+628123456789",
      "app": {
        "id": "wa",
        "name": "WhatsApp"
      },
      "country": {
        "id": 6,
        "name": "Indonesia",
        "iso": "ID",
        "calling_code": "+62"
      },
      "price": 2700,
      "currency": "IDR",
      "otps": [
        {
          "id": "OTP-C2D8F9A1",
          "code": "739733",
          "message": "Your WhatsApp code is 739733",
          "sender": "WhatsApp",
          "received_at": "2026-10-08T10:07:31.000Z"
        }
      ],
      "can_cancel": false,
      "cancel_available_at": "2026-10-08T10:09:00.000Z",
      "created_at": "2026-10-08T10:06:00.000Z",
      "expires_at": "2026-10-08T10:26:00.000Z"
    }
  }
}
ERRORS

Predictable error responses

Use error.code in your application logic and message for human-readable context.

HTTPCodeMeaning
400INVALID_OFFERThe request, query, or body is invalid.
401INVALID_API_KEYThe Bearer API key is missing or invalid.
402INSUFFICIENT_BALANCEThe wallet does not have enough balance.
404ORDER_NOT_FOUNDThe requested resource does not exist.
409OUT_OF_STOCK / OFFER_EXPIREDRefresh prices or choose another offer.
429RATE_LIMITEDWait for Retry-After before trying again.
503SERVICE_UNAVAILABLETemporary upstream or platform interruption.
ERROR · 409 Conflict
{
  "error": {
    "code": "OUT_OF_STOCK",
    "message": "No numbers are currently available.",
    "retryable": false
  }
}
Rate limits

Read RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset on every response. A 429 response also includes Retry-After.