Send OTP


Endpoint

For sending messages, the base url is:

@bulk_base/api/v1

Method

Method URI
POST /send-otp

Request Params

Param Type Description
sender string (required) The Sender ID to send the message with.
phone string (required) Phone Number to receive the OTP (same format rules as SMS).
expiry_time integer (optional) OTP validity in seconds, 60 to 3600. Defaults to 300 (5 minutes).
character_type string (optional) numeric (default), alpha or mixed.
length integer (optional) OTP length, 4 to 10. Defaults to 6.
correlator string (optional) Unique identifier generated by your system for tracking.
endpoint string (optional) Your delivery receipt endpoint.

Sample Request Header

Content-Type: application/json
Accept: application/json
Authorization: Bearer YOUR_TOKEN

{warning} Accept: application/json is required. Without it, an authentication failure returns a 302 redirect to the HTML login page instead of a JSON 401.

{info} The message text is generated by us and cannot be customised. It reads: Your OTP is: 123456. It will expire in 5 minutes. Any message field you send is ignored.

{warning} An OTP is issued to one number, so this endpoint takes a single message. A batch body is rejected with 422. Values outside the ranges above are also rejected with 422 and nothing is sent.

{info} alpha and mixed codes leave out characters that are easily misread off a screen (I, O, L, 0, 1), and mixed is upper case only. Verification is not case sensitive, so a recipient typing lower case still succeeds.

{info} Asking again for the same number while a code is still comfortably alive resends the same code rather than issuing a new one, so a retry does not invalidate the message the recipient is holding. A newer code always retires an older one, so only one code per number is ever valid.

Sample Request Body

{
    "sender": "BONGATECH",
    "phone": "2547XXXXXXXX",
    "expiry_time": 300,
    "character_type": "numeric",
    "length": 6,
    "correlator": "otp-789"
}

Sample Success Response

StatusCode 200

Content

{
    "status": true,
    "message": "Message successfully queued!",
    "data": {
        "correlator": 2,
        "uniqueId": "unique-string",
        "phone": "254XXXXXXXXX",
        "sms_units": 1,
        "expires_at": "2026-01-01 09:20:02",
        "expiry_time": 300
    }
}

Verify OTP

The Verify OTP endpoint allows you to confirm whether a one-time password (OTP) sent to a user’s phone is valid.
This step is typically used during login, registration, or sensitive transactions to ensure that the user has access
to the phone number they provided. The OTP must match the most recent, unexpired code issued to that phone number.

Method

Method URI
POST /verify-otp

Request Params

Param Type Description
phone string (required) Phone number the OTP was sent to, in the same format as when requesting.
otp string (required) The OTP entered by the user.

Request Body

{
    "phone": "07XXXXXXXX",
    "otp": "482195"
}

Sample Success Response

StatusCode 200

Content

{
    "status": true,
    "message": "OTP verified successfully"
}

Verification Failures

Each outcome carries the HTTP status that matches it. A successful verification is the only 2xx, so you can rely on the status code as well as the status field.

Status message Meaning
404 OTP not found No live OTP exists for that number on your account.
410 OTP expired The OTP was issued but is past its expiry time.
422 Invalid OTP A live OTP exists but the code does not match.
429 Too many incorrect attempts The allowed wrong guesses have been used up.

Content

{
    "status": false,
    "message": "Invalid OTP",
    "attempts_remaining": 3
}

{info} A code allows 5 wrong guesses. attempts_remaining tells you how many are left; at zero the code is retired and a new one must be requested. Expiry, exhaustion and a successful verification all retire the code, so it can never be presented twice.

{info} The number may be sent in any accepted format. 0712345678 and 254712345678 are the same recipient, so a code requested with one can be verified with the other.

Delivery Receipts Structure

All Requested delivery receipts will be delivered via a POST method with the below structure.

{
    "phone": "254XXXXXXXXX",
    "correlator": 2,
    "uniqueId": "unique-string",
    "deliveryStatus": "DeliveredToTerminal",
    "deliveryTime": "2020-01-01 00:00:00"
}