Create a Manual Phone Validation
Endpoint
POST https://api.verifik.co/v2/phone-validations/manual
Creates a standalone Phone Validation and sends an OTP by SMS or WhatsApp. No project or projectFlow is required. This is the same endpoint used by Smart Tools → WhatsApp / SMS Messages.
After a successful send, continue with Validate a Phone Validation.
Billing
Credits are checked before the message is sent, using country SMS/WhatsApp pricing. You are charged only when the OTP is actually delivered (sent: true).
Headers
| Name | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer {YOUR_ACCESS_TOKEN} |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | National number only (spaces are stripped). Do not include the dial code here. |
countryCode | string | Yes | Dial code starting with + (e.g. +57). Always send this; show it to the user with the phone (e.g. +57 3001234567). |
phoneGateway | string | Yes | sms or whatsapp. |
title | string | No | Company / sender name in the template (1–15 characters). Defaults to your client name. Used by WhatsApp flow2 and SMS copy. |
language | string | No | Template language (en, es, …). Selects flow2_en / flow2_es when whatsappTemplate is flow2. |
whatsappTemplate | string | No | WhatsApp only. authentication (default) or flow2. Ignored for SMS. |
force | boolean | No | When true, bypasses the ~2 minute resend cooldown for the same phone + gateway. Use for an explicit “Resend” action. |
ipAddress | string | No | Optional client IP for auditing. |
WhatsApp templates
whatsappTemplate | Meta template | Notes |
|---|---|---|
authentication (default) | authentication | OTP in body + button URL parameter. |
flow2 | flow2_es / flow2_en | Branded header/body (title as section) + code + action; language picks the variant. |
{
"phone": "3001234567",
"countryCode": "+57",
"phoneGateway": "whatsapp",
"title": "Company ABC",
"language": "es",
"whatsappTemplate": "flow2"
}
Request examples
- cURL
- Node.js
- Python
- PHP
curl -X POST "https://api.verifik.co/v2/phone-validations/manual" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": "3001234567",
"countryCode": "+57",
"phoneGateway": "whatsapp",
"title": "Company ABC",
"language": "en"
}'
import axios from "axios";
const { data } = await axios.post(
"https://api.verifik.co/v2/phone-validations/manual",
{
phone: "3001234567",
countryCode: "+57",
phoneGateway: "whatsapp",
title: "Company ABC",
language: "en",
},
{
headers: {
Authorization: "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
}
);
console.log(data);
import requests
response = requests.post(
"https://api.verifik.co/v2/phone-validations/manual",
headers={
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
json={
"phone": "3001234567",
"countryCode": "+57",
"phoneGateway": "whatsapp",
"title": "Company ABC",
"language": "en",
},
)
print(response.json())
<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.verifik.co/v2/phone-validations/manual', [
'headers' => [
'Authorization' => 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type' => 'application/json',
],
'json' => [
'phone' => '3001234567',
'countryCode' => '+57',
'phoneGateway' => 'whatsapp',
'title' => 'Company ABC',
'language' => 'en',
],
]);
echo $response->getBody();
Success response
{
"data": {
"_id": "66f0a1b2c3d4e5f678901234",
"client": "66f0a1b2c3d4e5f678900000",
"source": "manual",
"type": "validation",
"status": "sent",
"countryCode": "+57",
"phone": "3001234567",
"phoneGateway": "whatsapp",
"phoneData": {
"title": "Company ABC"
},
"language": "en",
"expiresAt": "2026-07-29T22:50:00.000Z",
"sent": true,
"new": true
},
"signature": "...",
"id": "a1b2c"
}
OTP lifetime and resend
| Rule | Default | Notes |
|---|---|---|
| OTP TTL | 10 minutes | Returned as data.expiresAt on a successful send. After this time, verify returns 412 phoneValidation_has_expired. |
| Resend cooldown | ~2 minutes | A second send for the same client + phone + phoneGateway within ~2 minutes returns 409 otp_recently_sent (no new message). |
| Force resend | "force": true | Bypasses the 2-minute cooldown so you can send a new code immediately (e.g. UI “Resend”). Each successful send/resend resets expiresAt to +10 minutes. |
UI guidance
Show the destination as countryCode + national phone (e.g. +57 3001234567). Drive an expiry countdown from expiresAt, and enable “Resend” after the 2-minute cooldown (or always with force: true).
Common errors
| Status | Message / code | When |
|---|---|---|
| 403 | insufficient_credits | Prepaid balance is below the country price for SMS/WhatsApp. Nothing is sent. |
| 409 | otp_recently_sent | Cooldown: an OTP was already sent for this phone + gateway within ~2 minutes (and force was not true). |
| 409 | otp_not_sent | Provider send failed (message was not accepted). Distinct from cooldown. |
| 409 | MissingParameter | Required body fields missing or countryCode not in +digits form. |
| 403 | Forbidden | Communication SMS/WhatsApp feature not available on the account. |
Next step
Verify the code with PUT /v2/phone-validations.