Skip to content

Developers · REST API

One API for billing, payments and invoices.

Customers, plans, subscriptions, usage events, wallets, invoices, checkout and benefits under /api/v1, documented in OpenAPI 3.1. This page is the map; the API reference has every endpoint.

Developers · API keys
  • tola_test_•••• 7f2kLast used 2 min agoTest
  • tola_live_•••• m4abRotating · 24 h graceLive

Secrets are stored as SHA-256 hashes and shown once, at creation.

Illustrative example with sample data. No payment is created.

In short

How do I integrate TolaPay into my application?

Create a workspace and copy a tola_test_ key. From your backend, create a customer, then either subscribe them to a plan, send usage events, or open a hosted checkout and redirect them to it. Register a webhook endpoint, verify the Tola-Signature header on each delivery, and grant access when invoice.paid arrives. Everything runs against sandbox Khalti, eSewa and Stripe until you switch to a tola_live_ key.

Quickstart

5 steps to a paid invoice.

From an empty workspace to a paid, verified invoice. Examples use the public API conventions; IDs are samples.
  1. 01

    Create a customer

    Name and email are required. Store your own id in external_id so later calls can use it instead of TolaPay's.

    curl https://api.tola.com.np/api/v1/customers \
      -H "Authorization: Bearer $TOLA_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: customer-phewa-001" \
      -d '{
        "name": "Phewa Studio Pvt. Ltd.",
        "email": "accounts@phewa.example",
        "external_id": "phewa-001"
      }'
  2. 02

    Subscribe them to a plan

    Plans are set up once in the dashboard or the API. A subscription links a customer to a plan; collection_method decides how renewals are paid.

    curl https://api.tola.com.np/api/v1/subscriptions \
      -H "Authorization: Bearer $TOLA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "customer_id": "cus_7k2mq9vd4r8xw3ba6tyn",
        "plan_id": "plan_g4wv8t2kq6m1xr9zb3dn",
        "seats": 3,
        "collection_method": "invoice_link"
      }'
  3. 03

    Record usage or open a checkout

    Send one event per billable action, with a transaction_id that makes retries safe. Events accept your external_id as the customer. Or create a hosted checkout and redirect the customer to its url.

    curl https://api.tola.com.np/api/v1/events \
      -H "Authorization: Bearer $TOLA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "customer_id": "phewa-001",
        "event_name": "ai_minutes",
        "transaction_id": "call_8f41c2",
        "properties": { "minutes": 4.5 }
      }'
    # → 202 { "status": "processed", ... }
    # Same transaction_id again → 202 { "status": "duplicate" }
  4. 04

    Receive a verified webhook

    TolaPay signs every delivery. Verify Tola-Signature against the raw body, reject stale timestamps, then act on the event idempotently.

    Node.js
    import crypto from "node:crypto";
    
    // rawBody: the request body exactly as received (a string, not parsed JSON)
    export function verifyTolaSignature(header, rawBody, secret) {
      const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
      const expected = crypto
        .createHmac("sha256", secret) // your endpoint secret, whsec_…
        .update(`${parts.t}.${rawBody}`)
        .digest("hex");
    
      const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
      const valid =
        expected.length === parts.v1.length &&
        crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
      return fresh && valid;
    }
    
    // if (event.type === "invoice.paid") grant access for event.data.customer_id
  5. 05

    Track payment status

    Invoices and payments are queryable at any time. The dashboard's API log shows every request with its response.

    curl
    curl "https://api.tola.com.np/api/v1/invoices?customer_id=cus_7k2mq9vd4r8xw3ba6tyn&status=finalized" \
      -H "Authorization: Bearer $TOLA_API_KEY"
    
    # → { "data": [ { "id": "inv_…", "status": "finalized", "total_cents": 703764, ... } ],
    #     "page": 1, "limit": 50, "total": 1 }

Authentication

Test and live, kept apart.

Server-to-server calls use a bearer key. Sandbox Khalti, eSewa and Stripe sit behind test keys, your real merchant accounts behind live ones.
  • Authorization: Bearer tola_test_… or tola_live_…
  • Secrets are shown once, stored as SHA-256 hashes, and rotate with a 24-hour grace period.
  • Keys carry a role (viewer, support, finance, developer, admin, owner), capped at the role of the person who created them.
  • Every request appears in the dashboard's API log with its response.

Conventions/api/v1

Base URL
https://api.tola.com.np/api/v1
Authentication
Authorization: Bearer tola_test_… or tola_live_…. Secrets are shown once; afterwards only the prefix and last four characters.
Money
Integers in minor units in *_cents fields, with an ISO currency (default NPR). 1500000 = NPR 15,000.00.
IDs
Prefixed strings: cus_, plan_, sub_, evt_, inv_, pay_ and so on.
Idempotency
Send Idempotency-Key on any POST; the response is replayed for 24 hours. Usage events are also de-duplicated by transaction_id.
Errors
An HTTP status and { "error": { "type", "message", "param" } }, with types such as validation_error, unauthorized, not_found, conflict and rate_limited.
Rate limits
600 requests per minute per API key, with X-RateLimit-Limit and X-RateLimit-Remaining headers and Retry-After on 429.
Lists
page, limit (up to 200), q and status; responses carry data, page, limit and total.

Webhooks

Webhooks you can trust.

TolaPay POSTs each event to your endpoint with a Tola-Signature header: t=<unix seconds>,v1=<hex HMAC>.

  1. 01Split the header on commas and read t and v1.
  2. 02Compute HMAC-SHA256 over "<t>.<raw body>" with your endpoint secret (whsec_…).
  3. 03Compare the hex digest to v1 in constant time.
  4. 04Reject the request if t is more than five minutes from now.

Respond with any 2xx within 10 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 12 hours.

Webhook delivery · invoice.paid200 · 182 ms

POST https://api.chautari.example/tola

Tola-Event
invoice.paid
Tola-Signature
t=1792311420,v1=5f0c9e…b81a
User-Agent
Tola-Webhooks/1.0
{
  "id": "we_…",
  "type": "invoice.paid",
  "created_at": "2026-10-18T08:17:00Z",
  "data": {
    "number": "INV-2083-0142",
    "customer_id": "cus_7k2mq9vd4r8xw3ba6tyn",
    "total_cents": 703764,
    "currency": "NPR",
    "status": "paid"
  }
}

HMAC-SHA256 over the timestamp and raw body. Retried on failure.

Illustrative example with sample data. No payment is created.

Events you'll use most

  • customer.created
  • subscription.created
  • subscription.updated
  • subscription.cancelled
  • invoice.finalized
  • invoice.paid
  • invoice.failed
  • payment.succeeded
  • payment.refunded
  • usage.received
  • wallet.low_balance
  • spend.hard_limit_reached
  • benefit_grant.created
  • order.completed

Errors

Errors and retries.

Errors return an HTTP status and a body of {"error": {"type", "message", "param"}}. Send an Idempotency-Key on POSTs and retry safely: the same response is replayed for 24 hours.

Error types
typeStatusMeaning
validation_error400A field is missing or malformed; param names it.
unauthorized401Missing or invalid API key.
payment_required402The action needs a payment first.
forbidden403The key's role is too low for this call.
not_found404A referenced customer, plan or product doesn't exist.
conflict409The request conflicts with the current state.
idempotency_key_reused422Same Idempotency-Key, different body.
rate_limited429Over 600 requests a minute; wait for Retry-After.

Less code

Less code, when that's what you need.

Not every flow needs an API call. Links, the storefront and the portal work from the dashboard; the tola.js overlay embeds checkout with one script tag.
Embed checkout with tola.js
<script src="https://api.tola.com.np/tola.js" defer></script>

<a href="https://app.tola.com.np/l/tcl_your_link_secret" data-tola-checkout>
  Subscribe
</a>

<script>
  // Fired by tola.js: checkout.opened, checkout.closed, checkout.success
</script>

Questions

Developer questions.

The OpenAPI document is public at /api/v1/openapi.yaml, so you can generate a client in any language.

Can I integrate TolaPay into my SaaS application?

Yes. TolaPay has a REST API under /api/v1, documented in OpenAPI 3.1, covering customers, plans, subscriptions, usage events, wallets, invoices, checkout, benefits and analytics. Most integrations start with three calls: create a customer, record usage or open a checkout, and handle the invoice.paid webhook.

Does TolaPay provide a sandbox?

Yes. Every workspace has a test mode with separate tola_test_ API keys and sandbox Khalti, eSewa and Stripe, so you can run the full flow, from checkout to webhook, before switching to tola_live_ keys.

How are webhooks verified?

Each webhook carries a Tola-Signature header with a timestamp and an HMAC-SHA256 of the timestamp and raw body, computed with your endpoint secret. Compare it in constant time and reject anything older than five minutes. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours.

Start in test mode today.

Sandbox Khalti, eSewa and Stripe are ready the moment you create a workspace. Go live when your integration is.

Free while TolaPay is in public beta. You pay only your payment providers' own fees.