Skip to content
Documentation menu

Documentation

Server SDKs

Typed server-side client libraries for Node.js, Python, Go, and PHP, generated from the public OpenAPI spec and kept in sync with the API.

These SDKs wrap the same REST API with a typed client for your language, for the backend half of an integration. They are generated from the public OpenAPI spec, so they stay in sync with the API: the same four actions, send, verify, resend, and status, are available as methods.

Verifying from a phone is a different shape, and the client libraries for it are separate: see Mobile SDKs for iOS, Android, React Native, and Flutter. Those two halves meet at the exchange call below.

Every SDK authenticates with your server API key as a Bearer token and defaults to the base URL https://api.otp.com/api/v1. If your language is not listed, the REST API works everywhere.

The install lines below are deliberately unpinned, so each package manager resolves the latest release on its own. To pin one in production, take the version from that SDK’s repository README or releases page: those move with the release, and this page would not.

The verify examples below apply to every channel. When a send routes to WhatsApp the response may also carry an action_url: the user opens it to finish on chat, and completion says whether a code comes back for the same verifyOtp call or the user’s own message approves the OTP. See WhatsApp verification.

Node.js

npm install @otp.com/sdk-node
import { Configuration, OTPApi } from '@otp.com/sdk-node';

const api = new OTPApi(new Configuration({ accessToken: process.env.OTP_API_KEY }));

// send
// clientIp = the END USER's IP from your request context (e.g. req.ip), not your server's.
const sent = await api.sendOtp({ sendRequest: { recipient: '+14155552671', locale: 'en', clientIp: '81.2.69.142' } });

// verify
const result = await api.verifyOtp({ verifyRequest: { otpId: sent.otpId, code: '123456' } });
// result.matched === true

Methods: sendOtp, verifyOtp, resendOtp, getOtpStatus.

Python

Not on PyPI yet, so install from GitHub. The distribution is otp-sdk, the import is otp_sdk:

pip install "otp-sdk @ git+https://github.com/otp-com/sdk-python"

That tracks the default branch. To pin, append the tag you want from sdk-python (…/sdk-python@v1.2.3).

import otp_sdk

config = otp_sdk.Configuration(host="https://api.otp.com/api/v1")
config.access_token = "YOUR_API_KEY"

with otp_sdk.ApiClient(config) as client:
    api = otp_sdk.OTPApi(client)
    # client_ip = the END USER's IP from your request context, not your server's.
    sent = api.send_otp({"recipient": "+14155552671", "locale": "en", "client_ip": "81.2.69.142"})
    result = api.verify_otp({"otp_id": sent.otp_id, "code": "123456"})

Methods: send_otp, verify_otp, resend_otp, get_otp_status.

Go

go get github.com/otp-com/sdk-go
import (
    "context"
    "os"

    otp "github.com/otp-com/sdk-go"
)

client := otp.NewAPIClient(otp.NewConfiguration())
ctx := context.WithValue(context.Background(), otp.ContextAccessToken, os.Getenv("OTP_API_KEY"))

// send. SetClientIp = the END USER's IP from your request context, not your server's.
req := otp.NewSendRequest("+14155552671")
req.SetLocale("en")
req.SetClientIp("81.2.69.142")
sent, _, err := client.OTPAPI.SendOtp(ctx).SendRequest(*req).Execute()

// verify
result, _, err := client.OTPAPI.VerifyOtp(ctx).
    VerifyRequest(*otp.NewVerifyRequest(sent.GetOtpId(), "123456")).
    Execute()
// result.GetMatched() == true

Methods: SendOtp, VerifyOtp, ResendOtp, GetOtpStatus. The key travels on the context, so one client can serve several keys. Run go mod tidy after adding the import.

PHP

composer require otp-com/sdk-php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use OtpCom\Sdk\Api\OTPApi;
use OtpCom\Sdk\Configuration;
use OtpCom\Sdk\Model\SendRequest;
use OtpCom\Sdk\Model\VerifyRequest;

$config = Configuration::getDefaultConfiguration()->setAccessToken(getenv('OTP_API_KEY'));
$otp = new OTPApi(new GuzzleHttp\Client(), $config);

// send. client_ip = the END USER's IP from your request context, not your server's.
$sent = $otp->sendOtp(new SendRequest([
    'recipient' => '+14155552671',
    'locale' => 'en',
    'client_ip' => '81.2.69.142',
]));

// verify
$result = $otp->verifyOtp(new VerifyRequest([
    'otp_id' => $sent->getOtpId(),
    'code' => '123456',
]));
// $result->getMatched() === true

Methods: sendOtp, verifyOtp, resendOtp, getOtpStatus. Model constructors take the wire field names (otp_id, not otpId).

Exchanging a mobile verification

Every SDK from 1.2.0 onwards also exposes POST /verifications/exchange, the call your backend makes to turn the verification_token a mobile SDK produced into the recipient it proves. It lives on its own client rather than on the one above: VerificationsApi in Node, Python, and PHP, VerificationsAPI in Go. The method is exchangeVerification (exchange_verification in Python, ExchangeVerification in Go).

The key you pass matters here. A publishable key cannot exchange a verification, and that is the point: it is the server key, and only the server key, that learns who was verified.

More on the way

Additional SDKs and tools are being added. Building with an AI agent? The MCP server exposes the same actions as agent tools.