API Documentation

Overview

Authly is an OTP delivery API that sends one-time passwords via Telegram, WhatsApp and SMS with automatic fallback between channels.

Use API keys from the API Keys page to authenticate your requests. Each key has its own rate limit; if you hit the limit, you will receive 429 Too Many Requests.

Base configuration

  • Base URL: production URL of your Authly API, for example https://authly-api.reread.kz.
  • Authentication: header X-API-Key: YOUR_API_KEY.
  • Content-Type: application/json.
  • Idempotency: sending OTP for the same phone within TTL returns existing request id instead of creating a new one.

Quick start

  1. Create an API key on the API Keys page and copy its value once.
  2. Store the key securely in your backend or secrets manager.
  3. Use the key in the X-API-Key header in all requests below.

Send OTP

Endpoint: POST /api/v1/otp/send

Sends an OTP to the given phone number. By default Authly routes through the cheapest reliable channel and falls back automatically if delivery fails. Pass channel to pin a specific provider instead (or set a default channel per API key on the Keys page).

RequestJSON
{
  "phone": "+77011234567",
  "channel": "whatsapp"
}
Response 200JSON
{
  "request_id": "00000000-0000-0000-0000-000000000000",
  "status": "sent",
  "channel": "whatsapp"
}

Verify OTP

Endpoint: POST /api/v1/otp/verify

Verifies the code that the user received. After several failed attempts the request is locked for a short period.

RequestJSON
{
  "request_id": "00000000-0000-0000-0000-000000000000",
  "code": "123456"
}
Response 200 (success)JSON
{
  "verified": true
}
Response 200 (invalid code)JSON
{
  "verified": false,
  "attempts_remaining": 2
}

Code examples

cURL — send OTPbash
curl -X POST "https://authly-api.reread.kz/api/v1/otp/send" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "phone": "+77011234567"
  }'
cURL — verify OTPbash
curl -X POST "https://authly-api.reread.kz/api/v1/otp/verify" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "request_id": "00000000-0000-0000-0000-000000000000",
    "code": "123456"
  }'
Node.jsts
import fetch from "node-fetch";

const API_KEY = process.env.AUTHLY_API_KEY;
const BASE_URL = "https://authly-api.reread.kz";

export async function sendOtp(phone: string) {
  const res = await fetch(`${BASE_URL}/api/v1/otp/send`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": API_KEY ?? "",
    },
    body: JSON.stringify({ phone }),
  });

  if (!res.ok) {
    throw new Error(`Send OTP failed: ${res.status}`);
  }

  return res.json();
}

export async function verifyOtp(requestId: string, code: string) {
  const res = await fetch(`${BASE_URL}/api/v1/otp/verify`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": API_KEY ?? "",
    },
    body: JSON.stringify({ request_id: requestId, code }),
  });

  if (!res.ok) {
    throw new Error(`Verify OTP failed: ${res.status}`);
  }

  return res.json();
}
Pythonpython
import os

import requests

API_KEY = os.environ.get("AUTHLY_API_KEY")
BASE_URL = "https://authly-api.reread.kz"


def send_otp(phone: str) -> dict:
    resp = requests.post(
        f"{BASE_URL}/api/v1/otp/send",
        headers={
            "Content-Type": "application/json",
            "X-API-Key": API_KEY or "",
        },
        json={"phone": phone},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()


def verify_otp(request_id: str, code: str) -> dict:
    resp = requests.post(
        f"{BASE_URL}/api/v1/otp/verify",
        headers={
            "Content-Type": "application/json",
            "X-API-Key": API_KEY or "",
        },
        json={"request_id": request_id, "code": code},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()

AI Agents

This section describes how to integrate Authly with AI agents (LLMs, chatbots, workflow builders). The main rule is that the agent must call the API from your backend, never expose API keys to end users and never log OTP codes.

  • Define a single tool for your agent that sends an OTP and, optionally, verifies it when the user provides the code.
  • Store the API key in server-side configuration and inject it into the tool implementation, not into the tool parameters.
  • Instruct the agent to ask the user for their phone number and, later, for the code they received, but never to guess or generate codes.
  • Make sure logs and analytics do not store raw OTP codes; only store high-level status (sent, verified, failed).
Tool schema (OpenAI / Anthropic)json
{
  "name": "authly_send_and_verify_otp",
  "description": "Send and verify one-time passwords via Authly",
  "schema": {
    "type": "object",
    "properties": {
      "phone": {
        "type": "string",
        "description": "User phone number in international format, for example +77011234567"
      },
      "code": {
        "type": "string",
        "description": "Code that user reads from SMS, WhatsApp or Telegram when you need to verify",
        "nullable": true
      }
    },
    "required": ["phone"]
  }
}