BDApps Integration Guide

AppsPro is a subscription management platform built on top of the Dialog Axiata BDApps telecom API. It handles subscriber lifecycle, OTP-based checkout, SMS delivery, charging, and webhook forwarding — so you can focus on building your app.

How it works

1You create an app on AppsPro and link your BDApps credentials (applicationId + password).
2End-users subscribe through the hosted checkout page — OTP verification is handled automatically.
3Use the SDK to verify subscriptions from your backend. Configure webhooks to get real-time events.
4Send SMS, charge subscribers, and manage subscriptions through the BDApps Operations API.

Quickstart

1. Register & Create an App

Sign up on the AppsPro dashboard, then create a new app. In the BDApps settings, enter your applicationId and password from the BDApps TAP portal (developer.bdapps.com).

2. Get SDK Credentials

After creating your app, go to the Credentials tab. You'll get:

client_id

Username for SDK Basic Auth

client_secret

Password for SDK Basic Auth (shown once)

public_key

For unauthenticated app info lookup

signing_key

For webhook signature verification

3. Make Your First API Call

Verify a subscriber using Basic Auth with your SDK credentials:

bash
curl https://api.appspro.dev/api/v1/sdk/verify/tel:8801712345678 \
  -u "your_client_id:your_client_secret"

SDK Guide

Authentication

SDK endpoints use HTTP Basic Auth. Encode client_id:client_secret as a Base64 string and pass it in the Authorization header.

http
Authorization: Basic base64(client_id:client_secret)
warning

Keep credentials secure

Never expose your client_secret in client-side code. SDK calls should always be made from your server.

Verify Subscription

Check whether a subscriber has an active subscription. Pass either the BDApps subscriber ID (e.g. tel:8801XXXXXXXXX) or the local AppsPro UUID.

GET/api/v1/sdk/verify/{subscriber_id}

Returns subscription validity, subscriber details, and reason if invalid.

Response

json
{
  "valid": true,
  "subscriber": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "bdapps_subscriber_id": "tel:8801712345678",
    "phone_masked": "01712***678",
    "status": "active",
    "subscription_type": "bdapps",
    "frequency": "daily",
    "subscribed_at": "2026-05-11T10:30:00Z",
    "cancelled_at": null,
    "created_at": "2026-05-11T10:30:00Z"
  },
  "reason": null
}

List Subscribers

Paginated list of all subscribers for your app. Filter by status.

GET/api/v1/sdk/subscribers?page=1&limit=20&status=active

Returns paginated subscriber list with total count.

json
{
  "subscribers": [
    {
      "id": "...",
      "bdapps_subscriber_id": "tel:8801712345678",
      "phone_masked": "01712***678",
      "status": "active",
      "frequency": "daily",
      "subscribed_at": "2026-05-11T10:30:00Z"
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20
}

App Info (Public)

Retrieve public metadata for your app. No authentication required — only the public_key.

GET/api/v1/sdk/app-info?public_key=xxx

Returns app name, description, category, pricing model, and icon URL.No Auth

Checkout Flow

Hosted Checkout Page

AppsPro provides a hosted checkout page for end-user subscriptions. Share or embed the checkout URL in your app. Users subscribe via OTP — no integration work needed on your end.

https://yourdomain.com/s/{client_id}

Flow

  1. User visits /s/{client_id} — sees your app info
  2. User enters their Bangladeshi mobile number
  3. OTP is sent via BDApps SMS
  4. User verifies OTP on the checkout page
  5. Subscription is created — user is redirected to your configured checkout_redirect_url

Checkout Endpoints

These endpoints power the hosted checkout page. No authentication required.

GET/s/{client_id}/info

Get app info for the checkout UI (name, description, icon, pricing).No Auth

POST/s/{client_id}/otp/request

Send OTP to a phone number. Rate limited to 10/hour per phone.No Auth

json
// POST body
{ "phone": "01712345678" }

// Response
{
  "reference_no": "bdapps_ref_abc123",
  "status_code": "S1000",
  "status_detail": "Success"
}
POST/s/{client_id}/otp/verify

Verify OTP and create the subscription.No Auth

json
// POST body
{
  "reference_no": "bdapps_ref_abc123",
  "otp": "1234"
}

// Response
{
  "subscription_status": "REGISTERED",
  "subscriber_id": "tel:8801712345678",
  "local_subscriber_id": "550e8400-...",
  "redirect_url": "https://yourapp.com/welcome",
  "status_code": "S1000",
  "status_detail": "Success"
}
info

Phone number formats

Accepts 01XXXXXXXXX, 8801XXXXXXXXX, or +8801XXXXXXXXX — with or without spaces and dashes.

Webhooks

Configure a webhook URL in your app settings to receive real-time events from BDApps, forwarded through AppsPro.

Event Types

subscriber.createdA new subscriber registered via OTP
subscriber.cancelledA subscriber unsubscribed
sms.receivedIncoming SMS message from a subscriber
ussd.receivedIncoming USSD request from a subscriber

Subscription Event Payload

json
POST https://your-server.com/webhooks/texionapps

Headers:
  Content-Type: application/json
  X-Event-Type: subscriber.created
  X-Signature: sha256=abc123...

Body:
{
  "event": "subscriber.created",
  "data": {
    "timeStamp": "2026-05-11T10:30:00Z",
    "applicationId": "BDAPPS_123",
    "subscriberId": "tel:8801712345678",
    "status": "REGISTERED",
    "frequency": "daily",
    "internal_subscriber_id": "550e8400-..."
  }
}

SMS Event Payload

json
{
  "event": "sms.received",
  "data": {
    "applicationId": "BDAPPS_123",
    "sourceAddress": "tel:8801712345678",
    "message": "Hello from subscriber",
    "requestId": "req_xyz",
    "encoding": "0"
  }
}

Signature Verification

Every webhook request includes an X-Signature header containing an HMAC-SHA256 signature of the request body, signed with your app's signing_key. Always verify this signature before processing the payload.

python
import hmac, hashlib

def verify_signature(body: bytes, signature: str, signing_key: str) -> bool:
    expected = hmac.new(
        signing_key.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

BDApps Operations

These authenticated endpoints let you interact with BDApps directly through your AppsPro app. All require JWT authentication and app ownership.

SMS

POST/apps/{app_id}/bdapps/sms

Send SMS to specific phone numbers.

json
// POST body
{
  "destination": ["01712345678", "01898765432"],
  "message": "Hello from your app!",
  "source_address": null
}

// Response
{
  "sent_count": 2,
  "status_code": "S1000",
  "status_detail": "Success"
}
POST/apps/{app_id}/bdapps/sms/broadcast

Send SMS to all active subscribers.

json
// POST body
{ "message": "Daily update for all subscribers" }

// Response
{ "sent_count": 150, "status_code": "S1000", "status_detail": "Success" }

Charging (CAAS)

Content-as-a-Service endpoints for charging subscribers and querying balances.

POST/apps/{app_id}/bdapps/debit

Charge a subscriber's mobile account (BDT).

json
// POST body
{
  "phone": "01712345678",
  "amount": "10.00",
  "external_trx_id": "txn_abc123"
}

// Response
{ "status_code": "S1000", "status_detail": "Success" }
GET/apps/{app_id}/bdapps/balance/{phone}

Query a subscriber's chargeable balance.

GET/apps/{app_id}/bdapps/base-size

Get total subscriber base size for the app.

Subscription Management

GET/apps/{app_id}/bdapps/status/{phone}

Check subscription status for a phone number.

POST/apps/{app_id}/bdapps/subscribe

Manually subscribe a phone number.

POST/apps/{app_id}/bdapps/unsubscribe

Manually unsubscribe a phone number.

POST/apps/{app_id}/bdapps/test-connection

Test that your BDApps credentials are valid.

Manual OTP (from Dashboard)

POST/apps/{app_id}/bdapps/otp/request

Request OTP for a phone number.

POST/apps/{app_id}/bdapps/otp/verify

Verify OTP and create subscription.

Code Examples

Verify Subscription (Python)

python
import requests

response = requests.get(
    "https://api.appspro.dev/api/v1/sdk/verify/tel:8801712345678",
    auth=("your_client_id", "your_client_secret"),
)

data = response.json()
if data["valid"]:
    print(f"Active subscriber: {data['subscriber']['id']}")
else:
    print(f"Invalid: {data['reason']}")

Verify Subscription (JavaScript)

javascript
const credentials = btoa(`${clientId}:${clientSecret}`);

const res = await fetch(
  "https://api.appspro.dev/api/v1/sdk/verify/tel:8801712345678",
  {
    headers: {
      Authorization: `Basic ${credentials}`,
    },
  }
);

const data = await res.json();
if (data.valid) {
  console.log("Active subscriber:", data.subscriber.id);
} else {
  console.log("Invalid:", data.reason);
}

Handle Webhook (Node.js / Express)

javascript
import crypto from "crypto";
import express from "express";

const app = express();
app.use(express.raw({ type: "application/json" }));

app.post("/webhooks/texionapps", (req, res) => {
  const signature = req.headers["x-signature"];
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.SIGNING_KEY)
    .update(req.body)
    .digest("hex");

  if (!crypto.timingSafeEqual(
    Buffer.from(signature), Buffer.from(expected)
  )) {
    return res.status(401).send("Invalid signature");
  }

  const payload = JSON.parse(req.body);
  switch (payload.event) {
    case "subscriber.created":
      console.log("New subscriber:", payload.data.subscriberId);
      break;
    case "subscriber.cancelled":
      console.log("Unsubscribed:", payload.data.subscriberId);
      break;
    case "sms.received":
      console.log("SMS from:", payload.data.sourceAddress);
      break;
  }

  res.sendStatus(200);
});

Send SMS (cURL)

bash
curl -X POST https://api.appspro.dev/apps/{app_id}/bdapps/sms \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": ["01712345678"],
    "message": "Hello from your app!"
  }'

BDApps Status Codes

S1000Success
E1312Invalid phone number
E1325Invalid OTP
E403Insufficient permissions
E404Resource not found

All BDApps responses include status_code, status_detail, and a raw field with the full BDApps response.

WebSDK

Overview

The AppsPro WebSDK lets you embed a subscription widget directly in your website or mobile app without redirecting users to a separate page. The widget is injected as plain HTML into your container — no iframe, no postMessage. This makes it reliable in all mobile WebViews (Android/iOS) as well as standard browsers.

How it works

1Your page loads texionapps.js from our CDN
2AppsPro(publicKey) creates an SDK instance
3sdk.elements.create('subscribe', options) returns a SubscribeElement
4element.mount('#container') fetches app info and injects the widget HTML inline
5Phone input → OTP → success runs in the page; events fire directly on the element

Installation & Usage

1. Include the script

html
<script src="https://appspro.dev/sdk/v1/texionapps.js"></script>

2. Add a container and initialize

html
<!-- In your HTML -->
<div id="subscribe-box"></div>

<script>
  const sdk = AppsPro('YOUR_PUBLIC_KEY');

  const el = sdk.elements.create('subscribe', {
    buttonText:  'Subscribe Now',
    buttonColor: '#6366f1',   // any CSS hex color
    theme:       'dark',      // 'dark' | 'light'
    locale:      'en',
    compact:     false,       // smaller padding
    hideHeader:  false,       // hide app name header
  });

  el.mount('#subscribe-box');

  el.on('ready',   ()     => console.log('Widget ready'));
  el.on('success', (data) => {
    console.log('Subscribed!', data.subscriberId);
    // Verify on your server:
    // GET /api/v1/sdk/verify/{data.subscriberId}
  });
  el.on('error',   (err)  => console.error('Error:', err.message));
</script>

Options reference

javascript
sdk.elements.create('subscribe', {
  buttonText:   string,   // default: 'Subscribe Now'
  buttonColor:  string,   // default: '#6366f1'
  theme:        string,   // 'dark' | 'light', default: 'dark'
  locale:       string,   // default: 'en'
  compact:      boolean,  // default: false
  hideHeader:   boolean,  // default: false
  borderRadius: string,   // default: '12px'
  baseUrl:      string,   // override API base (dev only)
})
play_circleLive DemoInteractive

Events

EventPayloadDescription
ready{}Widget has rendered in the DOM
success{ subscriberId, localSubscriberId, redirectUrl }User subscribed successfully
error{ message }An error occurred in the flow
otp-sent{ phone }OTP was sent to the phone
payment-redirect{ url }Redirect URL is available
cancel{}User cancelled the flow

Flutter / WebView Integration

For Flutter apps, load the WebSDK inside a WebView using the webview_flutter package. The SDK automatically calls a JavaScript channel named AppsPro with every event, so no extra wiring is needed on the JS side.

dart
import 'dart:convert';
import 'package:webview_flutter/webview_flutter.dart';

class SubscribeWebView extends StatefulWidget {
  final String publicKey;
  const SubscribeWebView({required this.publicKey, super.key});

  @override
  State<SubscribeWebView> createState() => _SubscribeWebViewState();
}

class _SubscribeWebViewState extends State<SubscribeWebView> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel(
        'AppsPro',  // SDK calls window.AppsPro.postMessage() automatically
        onMessageReceived: (msg) {
          final data = jsonDecode(msg.message) as Map<String, dynamic>;
          if (data['type'] == 'success') {
            final subscriberId = data['data']['subscriberId'];
            Navigator.pop(context, subscriberId);
          }
        },
      )
      ..loadHtmlString(_buildHtml());
  }

  String _buildHtml() => """
<!DOCTYPE html><html><head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="https://appspro.dev/sdk/v1/texionapps.js"></script>
</head><body style="margin:0;background:#0f0f13">
<div id="sub"></div>
<script>
  const sdk = AppsPro('${widget.publicKey}');
  const el = sdk.elements.create('subscribe', { hideHeader: true });
  el.mount('#sub');
  // Events fire automatically to the AppsPro JavascriptChannel
</script></body></html>""";

  @override
  Widget build(BuildContext context) => WebViewWidget(controller: _controller);
}

AI Agent / LLM Docs

Copy the markdown below and paste it into your AI assistant (Claude, ChatGPT, Cursor, etc.) to give it full context about the AppsPro API.

docs.md
# AppsPro — BDApps Integration API Reference

Base URL: https://api.appspro.dev
Auth: Bearer <access_token> in Authorization header

All BDApps endpoints require:
- A registered AppsPro account
- An app created in the dashboard with BDApps credentials configured (base_uri, client_id, password)

---

## BDApps Operations

All endpoints: POST/GET /apps/{id}/bdapps/...
Replace {id} with your AppsPro app's numeric ID.

### SMS
POST /apps/{id}/bdapps/sms
Body: { "phone": "01XXXXXXXXX", "message": "Your text here" }

### Subscriber Balance
GET /apps/{id}/bdapps/balance/{phone}
Returns: { status_code, status_detail, chargeable_balance, raw }

### Direct Debit (Charge subscriber)
POST /apps/{id}/bdapps/debit
Body: { "phone": "01XXXXXXXXX", "amount": 5.00, "ext_trx_id": "unique-txn-id" }
Returns: { status_code, status_detail, internal_trx_id, reference_id, timestamp, raw }

### Subscribe
POST /apps/{id}/bdapps/subscribe/{phone}
Subscribes the phone number to your service.

### Unsubscribe
POST /apps/{id}/bdapps/unsubscribe/{phone}
Unsubscribes the phone number from your service.

### Subscription Status
GET /apps/{id}/bdapps/status/{phone}
Returns current subscription status for the number.

### OTP — Request
POST /apps/{id}/bdapps/otp/request
Body: { "phone": "01XXXXXXXXX" }
Returns: { status_code, status_detail, reference_no, raw }

### OTP — Verify
POST /apps/{id}/bdapps/otp/verify
Body: { "reference_no": "...", "otp": "1234" }
Returns: { status_code, status_detail, raw }

### USSD
POST /apps/{id}/bdapps/ussd
Body: { "phone": "01XXXXXXXXX", "message": "text", "session_id": "optional", "type": "optional" }

### Subscriber Base Size
GET /apps/{id}/bdapps/base-size
Returns total subscriber count for the service.

### Test Credentials
POST /apps/{id}/bdapps/test
Validates that the stored BDApps credentials are working.

---

## BDApps Documents

GET /apps/{id}/documents
  — List available documents for the app

GET /apps/{id}/documents/bank-verification
  — Generate bank earning verification HTML document

GET /apps/{id}/documents/service-faq?username=&password=&service_name=&service_id=&operator=
  — Generate BDApps service FAQ HTML document

---

## Response Format

All BDApps operation responses:
{
  "status_code": "S1000",      // "S1000" = success; any other = failure
  "status_detail": "Success",
  "raw": { ... }               // full BDApps API response
}

Error responses: HTTP 4xx with { "detail": "error message" }

---

## Notes

- Phone numbers must be Bangladeshi format: 01XXXXXXXXX (Robi/Airtel numbers for BDApps)
- ext_trx_id for debit must be unique per transaction
- BDApps credentials (base_uri, client_id, password) are set per-app in the AppsPro dashboard under App Settings
- All requests require a valid Bearer token from POST /auth/login → access_token