Home / Developers
Developer console

A console built for the people who integrate

Manage API keys, wire up signed webhooks, test deliveries, read delivery logs and watch usage — everything you need to run OTP routing in production, in one place.

REST + JSONHMAC-SHA256 webhooksSandbox on request
Webhook deliverieshttps://api.your-app.example/otp
activation.sms_received200
number.sms_received200
activation.expired500 · retry
activation.sms_received200
Test event sent from console200
What's inside

Tools for building, testing and operating

The console is the control plane for your integration. The API is the data plane. Both use the same account and wallet.

🔑

Multiple API keys

Create a key per environment, service or teammate. Keys use the or_live_ prefix, are shown once at creation, and can be revoked individually.

🪝

Webhook endpoints

Register HTTPS endpoints and choose the events each one receives — or subscribe to all of them.

🧪

Test-send

Send a signed test event to an endpoint and confirm your handler verifies and acknowledges it.

📜

Delivery log

Every attempt is logged with event type, HTTP status and a response excerpt. Failed deliveries are retried automatically with backoff.

♻️

Secret rotation

Rotate an endpoint's signing secret from the console whenever your security policy requires it.

📈

Usage analytics

Per-day API calls and errors and your top endpoints, to spot regressions and plan capacity.

Signature verification

Trust only what we signed

Every delivery includes X-OTPRoute-Signature in the form t=<unix>,v1=<hex>, plus X-OTPRoute-Event and a unique X-OTPRoute-Delivery ID.

  • Compute HMAC-SHA256 of <t>.<raw body> with the endpoint secret
  • Compare against v1 using a constant-time function
  • Reject timestamps outside your tolerance (for example 5 minutes)
  • Use the delivery ID to make your handler idempotent

Securing OTP webhooks with HMAC →

webhook handler
import hmac, hashlib, time, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["OTPROUTE_WEBHOOK_SECRET"]

def verified(header: str, body: bytes) -> bool:
    p = dict(x.split("=", 1) for x in header.split(","))
    if abs(time.time() - int(p["t"])) > 300:
        return False
    mac = hmac.new(SECRET.encode(), p["t"].encode() + b"." + body,
                   hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, p.get("v1", ""))

@app.post("/hooks/otproute")
def hook():
    if not verified(request.headers.get("X-OTPRoute-Signature", ""), request.get_data()):
        abort(401)
    evt = request.get_json()
    if evt["type"] == "activation.sms_received":
        handle_code(evt["data"]["id"], evt["data"]["code"])
    return "", 204
Webhook events

Events you can subscribe to

Each payload has the shape {"id": "evt_…", "type": "…", "created": <unix>, "data": {…}}. Failed deliveries are retried automatically with backoff.

EventFires whenData
activation.sms_receivedAn OTP is captured for an activation (and again if the code is updated)Activation object with code and sms_text
activation.expiredThe window closed without an SMS; the activation is refundedActivation object
activation.cancelledThe activation was cancelled; it is refundedActivation object
number.sms_receivedA dedicated number receives an inbound SMSMessage with from, to, text
number.renewedA dedicated number's term is renewed from the walletNumber object with new renews_at
number.releasedA dedicated number is released by you or at an unpaid term endNumber object
Limits

Rate limits and quotas

Every account is pay as you go and gets the same limits, applied per account across all keys. Exceeding the request rate returns 429 rate_limited with a Retry-After header.

AccountAPI requests / minAPI keysWebhook endpointsMonthly fee
Pay as you go (every account)300Up to 10Up to 5None

Webhook deliveries do not count toward your request rate. See pricing →

Quickstart

Live in five steps

Create an account

Sign up with email or Google, verify your email and turn on two-factor authentication. There is no monthly fee.

Generate an API key

In the console, create a key and store it as a secret — it is only shown once.

Top up your wallet

Add funds by card or USDT/USDC. Or ask support to enable a sandbox account first.

Register a webhook

Add your HTTPS endpoint, copy its secret and use test-send to confirm verification.

Order an activation

Call POST /v1/activations and read the code from the webhook or by polling.

🧰

Sandbox accounts

Sandbox accounts receive simulated numbers and codes through the same endpoints and webhook events, with no wallet spend. Use them to build and test your integration end to end, including CI runs.

Email [email protected] to enable sandbox mode on a development account.

Generate your first API key

Pay as you go with no monthly fee. Bring your own test flow and route your first OTP today.