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.
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).
{
"phone": "+77011234567",
"channel": "whatsapp"
}{
"request_id": "00000000-0000-0000-0000-000000000000",
"status": "sent",
"channel": "whatsapp"
}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.
{
"request_id": "00000000-0000-0000-0000-000000000000",
"code": "123456"
}{
"verified": true
}{
"verified": false,
"attempts_remaining": 2
}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 -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"
}'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();
}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()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.
{
"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"]
}
}