Nothing found

OTP examples

Examples of using the OTP API. A code is sent through send, checked through verify and, when needed, sent again through resend — all three work with the id returned by the first request.

Smallest possible request

Only the recipient number and request_quota_identification are mandatory — the identification of the requester the per-minute quota is bound to. We recommend their IP address. Without channel the code goes out over SMS.

POST /api/1.0/otp/send HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1" }

Custom code format

code_type picks the characters (int, string, combined), code_length the length (4–100). language selects the message language out of 100 supported ISO codes.

POST /api/1.0/otp/send HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "code_type": "combined", "code_length": 8, "language": "en" }

Autofilling the code on the web (WebOTP)

developer.mozilla.org/WebOTP_API

domain in the SMS object appends a line that lets the browser offer the code by itself. The domain must match the site where the code is entered.

POST /api/1.0/otp/send HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "channel": { "sms": { "sender_id": "gText", "sender_id_value": "BulkGate", "domain": "https://example.com" } } }

Channel cascade

The order of the keys is the order of the cascade. Here Viber is tried first, after 60 seconds without delivery RCS, and SMS last. An expiration below 60 is ignored.

POST /api/1.0/otp/send HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "channel": { "viber": { "sender": "BulkGate", "expiration": 60 }, "rcs": { "sender": "BulkGate", "expiration": 60 }, "sms": { "sender_id": "gText", "sender_id_value": "BulkGate" } } }

Code read out loud

Text2Voice reads the code. repeat says it again during the call so the recipient can write it down.

POST /api/1.0/otp/send HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "code_type": "int", "code_length": 4, "channel": { "text2voice": { "sender": "447820125799", "language": "en", "prefer_gender": "female", "repeat": 2 } } }

Limits against abuse

trials_available_* caps how many times one code can be resent and verified, daily_limit the number of requests per 24 hours. daily_limit_country additionally allows only the listed countries — any other one fails with country_is_disabled.

POST /api/1.0/otp/send HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "request_quota_number": 3, "trials_available_resend": 2, "trials_available_verification": 5, "daily_limit": 100, "daily_limit_country": { "gb": 100, "ie": 50 }, "expiration": 600 }

Verifying the code

id is the value from the send response. The response returns verified; false means an invalid code, not a failed request.

POST /api/1.0/otp/verify HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "id": "otp-65cca8b4b6a6a2.24308291", "code": "123456" }

Resending the code

Generates a new code for the same request and sends it over the same channel. Two resends must be 60 seconds apart.

POST /api/1.0/otp/resend HTTP/1.1 Host: portal.bulkgate.com Content-Type: application/json { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "id": "otp-65cca8b4b6a6a2.24308291" }

Code examples

Below is the same send request in different programming languages:

The meaning of the individual parameters is described in the specification.

PHP

php.net

$context = stream_context_create(['http' => [ 'method' => 'POST', 'header' => 'Content-Type: application/json', 'content' => json_encode([ 'application_id' => 451, 'application_token' => 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj', 'number' => '447820125799', 'request_quota_identification' => '127.0.0.1', 'language' => 'en', 'code_length' => 6, 'channel' => [ 'sms' => [ 'sender_id' => 'gText', 'sender_id_value' => 'BulkGate', ], ], ], JSON_THROW_ON_ERROR), 'timeout' => 30, 'ignore_errors' => true, ]]); $body = file_get_contents('https://portal.bulkgate.com/api/1.0/otp/send', false, $context); if ($body === false) { throw new RuntimeException('BulkGate API did not respond.'); } $response = json_decode($body, true, flags: JSON_THROW_ON_ERROR); if (isset($response['error'])) { throw new RuntimeException("BulkGate: {$response['error']} ({$response['type']})"); } print_r($response['data']);

PHP cURL

php.net/curl

$curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => 'https://portal.bulkgate.com/api/1.0/otp/send', CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'application_id' => 451, 'application_token' => 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj', 'number' => '447820125799', 'request_quota_identification' => '127.0.0.1', 'language' => 'en', 'code_length' => 6, 'channel' => [ 'sms' => [ 'sender_id' => 'gText', 'sender_id_value' => 'BulkGate', ], ], ], JSON_THROW_ON_ERROR), ]); $body = curl_exec($curl); $error = curl_error($curl); curl_close($curl); if ($error !== '') { throw new RuntimeException("BulkGate API: $error"); } print_r(json_decode($body, true, flags: JSON_THROW_ON_ERROR));

PHP Guzzle

docs.guzzlephp.org

// composer require guzzlehttp/guzzle use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; $client = new Client(['timeout' => 30, 'http_errors' => false]); try { $result = $client->post('https://portal.bulkgate.com/api/1.0/otp/send', ['json' => [ 'application_id' => 451, 'application_token' => 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj', 'number' => '447820125799', 'request_quota_identification' => '127.0.0.1', 'language' => 'en', 'code_length' => 6, 'channel' => [ 'sms' => [ 'sender_id' => 'gText', 'sender_id_value' => 'BulkGate', ], ], ]]); } catch (GuzzleException $e) { throw new RuntimeException('BulkGate: ' . $e->getMessage(), previous: $e); } print_r(json_decode((string) $result->getBody(), true, flags: JSON_THROW_ON_ERROR));

Go

go.dev

package main import ( "fmt" "io" "net/http" "strings" ) func main() { url := "https://portal.bulkgate.com/api/1.0/otp/send" payload := strings.NewReader(`{"application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": {"sms": {"sender_id": "gText", "sender_id_value": "BulkGate"}}}`) req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(body)) }

Python

python.org

# pip install requests import requests response = requests.post('https://portal.bulkgate.com/api/1.0/otp/send', json={ 'application_id': 451, 'application_token': 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj', 'number': '447820125799', 'request_quota_identification': '127.0.0.1', 'language': 'en', 'code_length': 6, 'channel': { 'sms': { 'sender_id': 'gText', 'sender_id_value': 'BulkGate', }, }, }, timeout=30) data = response.json() if 'error' in data: raise RuntimeError(f"BulkGate: {data['error']} ({data['type']})") print(data['data']['id'])

Node.js

nodejs.org

// Node.js 18+ has fetch globally. Top-level await requires ESM (.mjs or "type": "module"). const response = await fetch("https://portal.bulkgate.com/api/1.0/otp/send", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": { "sms": { "sender_id": "gText", "sender_id_value": "BulkGate" } } }), }); const result = await response.json(); if (result.error) { throw new Error(`BulkGate: ${result.error} (${result.type})`); } console.log(result.data.id);

Java

java.net.http.HttpClient

import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://portal.bulkgate.com/api/1.0/otp/send")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(""" { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": { "sms": { "sender_id": "gText", "sender_id_value": "BulkGate" } } }""")) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body());

Kotlin

kotlinlang.org

import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse val payload = """ { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": { "sms": { "sender_id": "gText", "sender_id_value": "BulkGate" } } } """.trimIndent() val request = HttpRequest.newBuilder() .uri(URI.create("https://portal.bulkgate.com/api/1.0/otp/send")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(payload)) .build() val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) println("${response.statusCode()} ${response.body()}")

C# .NET

learn.microsoft.com

using System.Net.Http; using System.Text; var payload = """ { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": { "sms": { "sender_id": "gText", "sender_id_value": "BulkGate" } } } """; using var client = new HttpClient(); var response = await client.PostAsync( "https://portal.bulkgate.com/api/1.0/otp/send", new StringContent(payload, Encoding.UTF8, "application/json")); Console.WriteLine($"{(int) response.StatusCode} {await response.Content.ReadAsStringAsync()}");

C++ libcurl

curl.se/libcurl

#include <curl/curl.h> #include <iostream> #include <string> static size_t collect(char *data, size_t size, size_t nmemb, std::string *out) { out->append(data, size * nmemb); return size * nmemb; } int main() { const std::string payload = R"json( { "application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": { "sms": { "sender_id": "gText", "sender_id_value": "BulkGate" } } } )json"; CURL *curl = curl_easy_init(); if (curl == nullptr) { std::cerr << "curl_easy_init failed\n"; return 1; } curl_slist *headers = curl_slist_append(nullptr, "Content-Type: application/json"); std::string body; curl_easy_setopt(curl, CURLOPT_URL, "https://portal.bulkgate.com/api/1.0/otp/send"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, collect); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body); const CURLcode result = curl_easy_perform(curl); long status = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); curl_slist_free_all(headers); curl_easy_cleanup(curl); if (result != CURLE_OK) { std::cerr << curl_easy_strerror(result) << "\n"; return 1; } std::cout << status << " " << body << "\n"; return 0; }

Ruby

ruby-lang.org

require 'json' require 'net/http' uri = URI('https://portal.bulkgate.com/api/1.0/otp/send') response = Net::HTTP.post(uri, { 'application_id' => 451, 'application_token' => 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj', 'number' => '447820125799', 'request_quota_identification' => '127.0.0.1', 'language' => 'en', 'code_length' => 6, 'channel' => { 'sms' => { 'sender_id' => 'gText', 'sender_id_value' => 'BulkGate' } } }.to_json, 'Content-Type' => 'application/json') puts "#{response.code} #{response.body}"

Bash cURL

curl.se

curl --request POST \ --url https://portal.bulkgate.com/api/1.0/otp/send \ --header 'Content-Type: application/json' \ --data '{"application_id": 451, "application_token": "NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj", "number": "447820125799", "request_quota_identification": "127.0.0.1", "language": "en", "code_length": 6, "channel": {"sms": {"sender_id": "gText", "sender_id_value": "BulkGate"}}}'
Was this page helpful?