curl --request POST \
--url https://api.otpbay.com/v1/messages/telegram \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"to": "+14155552671",
"metadata": {
"user_id": "usr_1042"
}
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({to: '+14155552671', metadata: {user_id: 'usr_1042'}})
};
fetch('https://api.otpbay.com/v1/messages/telegram', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.otpbay.com/v1/messages/telegram"
payload = {
"to": "+14155552671",
"metadata": { "user_id": "usr_1042" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.otpbay.com/v1/messages/telegram",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'to' => '+14155552671',
'metadata' => [
'user_id' => 'usr_1042'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.otpbay.com/v1/messages/telegram"
payload := strings.NewReader("{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.otpbay.com/v1/messages/telegram")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.otpbay.com/v1/messages/telegram")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}"
response = http.request(request)
puts response.read_bodyusing RestSharp;
var options = new RestClientOptions("https://api.otpbay.com/v1/messages/telegram");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("Authorization", "Bearer <token>");
request.AddJsonBody("{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}", false);
var response = await client.PostAsync(request);
Console.WriteLine("{0}", response.Content);
{
"sid": "TG6650c52ad3e4f5a6b7c8d9e0",
"status": "sent",
"verification_status": null,
"to": "+14155552671",
"code_length": 6,
"cost": "0.02",
"date_created": "2026-09-26T10:22:40.011Z",
"expires_at": "2026-09-26T10:27:40.011Z",
"delivered_at": null,
"verified_at": null,
"metadata": {
"user_id": "usr_1042"
}
}{
"error": {
"code": "INVALID_JSON",
"message": "Invalid JSON body"
}
}{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key."
}
}{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Insufficient balance for this SMS destination."
}
}{
"error": {
"code": "CHANNEL_DISABLED",
"message": "SMS is turned off for this project."
}
}{
"error": {
"code": "TELEGRAM_REJECTED",
"message": "Telegram rejected the request (PHONE_NUMBER_NOT_FOUND)."
}
}{
"error": {
"code": "TELEGRAM_UNAVAILABLE",
"message": "Telegram is unavailable right now. Try again later."
}
}Send a Telegram code
Sends a one-time code to the Telegram account registered to to. Pass your own code, or leave it out and Telegram generates one with the code length from your Telegram settings. If Telegram doesn’t deliver the code before it expires, the charge is refunded.
curl --request POST \
--url https://api.otpbay.com/v1/messages/telegram \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"to": "+14155552671",
"metadata": {
"user_id": "usr_1042"
}
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({to: '+14155552671', metadata: {user_id: 'usr_1042'}})
};
fetch('https://api.otpbay.com/v1/messages/telegram', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.otpbay.com/v1/messages/telegram"
payload = {
"to": "+14155552671",
"metadata": { "user_id": "usr_1042" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.otpbay.com/v1/messages/telegram",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'to' => '+14155552671',
'metadata' => [
'user_id' => 'usr_1042'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.otpbay.com/v1/messages/telegram"
payload := strings.NewReader("{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.otpbay.com/v1/messages/telegram")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.otpbay.com/v1/messages/telegram")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}"
response = http.request(request)
puts response.read_bodyusing RestSharp;
var options = new RestClientOptions("https://api.otpbay.com/v1/messages/telegram");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("Authorization", "Bearer <token>");
request.AddJsonBody("{\n \"to\": \"+14155552671\",\n \"metadata\": {\n \"user_id\": \"usr_1042\"\n }\n}", false);
var response = await client.PostAsync(request);
Console.WriteLine("{0}", response.Content);
{
"sid": "TG6650c52ad3e4f5a6b7c8d9e0",
"status": "sent",
"verification_status": null,
"to": "+14155552671",
"code_length": 6,
"cost": "0.02",
"date_created": "2026-09-26T10:22:40.011Z",
"expires_at": "2026-09-26T10:27:40.011Z",
"delivered_at": null,
"verified_at": null,
"metadata": {
"user_id": "usr_1042"
}
}{
"error": {
"code": "INVALID_JSON",
"message": "Invalid JSON body"
}
}{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key."
}
}{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Insufficient balance for this SMS destination."
}
}{
"error": {
"code": "CHANNEL_DISABLED",
"message": "SMS is turned off for this project."
}
}{
"error": {
"code": "TELEGRAM_REJECTED",
"message": "Telegram rejected the request (PHONE_NUMBER_NOT_FOUND)."
}
}{
"error": {
"code": "TELEGRAM_UNAVAILABLE",
"message": "Telegram is unavailable right now. Try again later."
}
}Authorizations
Your project API key, sent as Authorization: Bearer otp_live_.... Create keys on the API Keys page of the dashboard.
Body
Destination phone number in international format. OTPBay normalizes it to E.164, so +1 (415) 555-2671 becomes +14155552671.
"+14155552671"
Code to send, 4–8 digits. Leave it out and Telegram generates a code with the code length from your Telegram settings; then check it with Verify a Telegram code.
^\d{4,8}$"482913"
Public http or https URL that receives status webhooks. Localhost, private and link-local addresses are rejected. Defaults to the channel's default callback from project settings.
2048"https://example.com/webhooks/otpbay"
Response
The code was handed to Telegram.
Unique Telegram code ID.
^TG[a-f0-9]{24}$"TG6650c52ad3e4f5a6b7c8d9e0"
Delivery status reported by Telegram. failed means the code never reached Telegram.
sending, sent, delivered, read, expired, revoked, failed Result of the latest code check, or null before the first check.
code_valid, code_invalid, code_max_attempts_exceeded, expired, null Destination phone number in international format. OTPBay normalizes it to E.164, so +1 (415) 555-2671 becomes +14155552671.
"+14155552671"
Number of digits in the code.
6
What this code currently costs. 0.00 after a refund.
^\d+\.\d{2}$"0.02"
Why the code couldn't be sent. Only present when there is one.