curl -X POST https://verification.didit.me/v3/phone/check/ \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"phone_number": "+14155552671",
"code": "123456",
"voip_number_action": "DECLINE"
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.phone.check_code(
phone_number="+14155552671",
code="123456",
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.phone.checkCode({
phone_number: "+14155552671",
code: "123456",
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Phone.CheckCode(ctx, didit.PhoneCheckCodeParams{
PhoneNumber: "+14155552671",
Code: "123456",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import me.didit.Didit;
import me.didit.types.PhoneCheckCodeParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.phone().checkCode(PhoneCheckCodeParams.builder()
.phoneNumber("+14155552671")
.code("123456")
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->phone->checkCode([
'phone_number' => '+14155552671',
'code' => '123456',
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.phone.check_code(
phone_number: "+14155552671",
code: "123456"
)
puts result.to_husing System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.Phone.CheckCodeAsync(new PhoneCheckCodeParams
{
PhoneNumber = "+14155552671",
Code = "123456",
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
"status": "Approved",
"message": "The verification code is correct.",
"phone": {
"status": "Approved",
"phone_number_prefix": "+1",
"phone_number": "4155552671",
"full_number": "+14155552671",
"country_code": "US",
"country_name": "United States",
"carrier": {
"name": "AT&T",
"type": "mobile"
},
"is_disposable": false,
"is_virtual": false,
"verification_method": "whatsapp",
"verification_attempts": 1,
"verified_at": "2026-06-12T01:24:47.311323Z",
"warnings": [],
"lifecycle": [
{
"type": "PHONE_VERIFICATION_MESSAGE_SENT",
"timestamp": "2026-06-12T01:23:39.580554+00:00",
"details": {
"status": "Success",
"reason": null,
"channel": "whatsapp",
"actual_channel": "whatsapp",
"attempt": 0,
"delivery_status": "delivered"
},
"fee": 0.0509
},
{
"type": "PHONE_DELIVERY_DELIVERED",
"timestamp": "2026-06-12T01:23:43.118220+00:00",
"details": {
"channel": "whatsapp",
"status": "delivered",
"attempt": 0
},
"fee": 0
},
{
"type": "VALID_CODE_ENTERED",
"timestamp": "2026-06-12T01:24:47.311201+00:00",
"details": {
"code_tried": "123456",
"status": "Approved"
},
"fee": 0
},
{
"type": "PHONE_VERIFICATION_APPROVED",
"timestamp": "2026-06-12T01:24:47.384292+00:00",
"details": null,
"fee": 0
}
],
"matches": []
},
"vendor_data": "user-1234",
"metadata": null,
"created_at": "2026-06-12T01:24:47.401719+00:00"
}Check Phone OTP API: Validate SMS Code
POST /v3/phone/check/ to validate a phone OTP with carrier type, virtual number, and disposable detection. Pay-per-use pricing, no monthly minimums.
curl -X POST https://verification.didit.me/v3/phone/check/ \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"phone_number": "+14155552671",
"code": "123456",
"voip_number_action": "DECLINE"
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.phone.check_code(
phone_number="+14155552671",
code="123456",
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.phone.checkCode({
phone_number: "+14155552671",
code: "123456",
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Phone.CheckCode(ctx, didit.PhoneCheckCodeParams{
PhoneNumber: "+14155552671",
Code: "123456",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import me.didit.Didit;
import me.didit.types.PhoneCheckCodeParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.phone().checkCode(PhoneCheckCodeParams.builder()
.phoneNumber("+14155552671")
.code("123456")
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->phone->checkCode([
'phone_number' => '+14155552671',
'code' => '123456',
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.phone.check_code(
phone_number: "+14155552671",
code: "123456"
)
puts result.to_husing System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.Phone.CheckCodeAsync(new PhoneCheckCodeParams
{
PhoneNumber = "+14155552671",
Code = "123456",
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
"status": "Approved",
"message": "The verification code is correct.",
"phone": {
"status": "Approved",
"phone_number_prefix": "+1",
"phone_number": "4155552671",
"full_number": "+14155552671",
"country_code": "US",
"country_name": "United States",
"carrier": {
"name": "AT&T",
"type": "mobile"
},
"is_disposable": false,
"is_virtual": false,
"verification_method": "whatsapp",
"verification_attempts": 1,
"verified_at": "2026-06-12T01:24:47.311323Z",
"warnings": [],
"lifecycle": [
{
"type": "PHONE_VERIFICATION_MESSAGE_SENT",
"timestamp": "2026-06-12T01:23:39.580554+00:00",
"details": {
"status": "Success",
"reason": null,
"channel": "whatsapp",
"actual_channel": "whatsapp",
"attempt": 0,
"delivery_status": "delivered"
},
"fee": 0.0509
},
{
"type": "PHONE_DELIVERY_DELIVERED",
"timestamp": "2026-06-12T01:23:43.118220+00:00",
"details": {
"channel": "whatsapp",
"status": "delivered",
"attempt": 0
},
"fee": 0
},
{
"type": "VALID_CODE_ENTERED",
"timestamp": "2026-06-12T01:24:47.311201+00:00",
"details": {
"code_tried": "123456",
"status": "Approved"
},
"fee": 0
},
{
"type": "PHONE_VERIFICATION_APPROVED",
"timestamp": "2026-06-12T01:24:47.384292+00:00",
"details": null,
"fee": 0
}
],
"matches": []
},
"vendor_data": "user-1234",
"metadata": null,
"created_at": "2026-06-12T01:24:47.401719+00:00"
}Handle provider outages
If the phone provider is temporarily unavailable, this endpoint returns502 with a stable machine-readable code:
{
"detail": "The phone verification provider is temporarily unavailable.",
"code": "phone_provider_unavailable"
}
Intelligence add-on
Setenable_enrichment to true to also run phone intelligence on the number: a risk score, the line type, whether the number was recently ported and a trust index.
The add-on is billed at $0.05 per request at list price, on top of the price of the check.
The request returns 403 when your balance cannot cover it.
Four fields decide what the signals do.
Each is NO_ACTION (the default) or DECLINE, and each only takes effect when enable_enrichment is true.
| Field | Declines when |
|---|---|
phone_intelligence_score_action | The risk score is above the threshold. |
phone_type_risk_action | The number is a high-risk line type. |
recent_port_action | The number was ported to another carrier recently. |
low_phone_trust_action | The trust index is at or below the threshold. |
NO_ACTION, the signal is still returned in phone.phone_intelligence, so you can apply your own rule.Authorizations
Your application's API key, from Developers -> API keys in the Business Console. The primary key has full access. A named key can be scoped: none, read or write per resource, limited to some workflows or to approved sessions, to a list of IP addresses, and to an expiry date. 401 means the key is wrong, revoked or expired; 403 means no key was sent, the key has no access to this resource or action, or the request came from an address outside its IP list; 404 on a session route means the session is outside the key's workflows or statuses. A key without media access receives image, video and PDF URLs as null, and a key without sessions write receives session links and tokens as null. See https://docs.didit.me/console/api-keys.
Body
The same phone number used in the matching POST /v3/phone/send/ call, in E.164 format. This is what links the check to the send.
20"+14155552671"
What to do when the same number was already used by a different user (different vendor_data) of your application. DECLINE flips the final status to Declined; NO_ACTION records the risk in phone.warnings and fills phone.matches.
NO_ACTION, DECLINE When true, also run phone intelligence on the number: a risk score, the line type, whether it was recently ported and a trust index. Billed as one phone_intelligence_api unit per request ($0.05 at list price) in addition to this endpoint's own price. The request returns 403 when your balance cannot cover it.
false
Action when the phone intelligence risk score is above the threshold. DECLINE flips the final status to Declined; NO_ACTION records the signal without declining. Only takes effect when enable_enrichment is true.
NO_ACTION, DECLINE Action when the phone intelligence trust index is at or below the threshold. DECLINE flips the final status to Declined; NO_ACTION records the signal without declining. Only takes effect when enable_enrichment is true.
NO_ACTION, DECLINE Action when the social footprint check finds the number registered on none of the platforms it covers. Only NO_ACTION and DECLINE are accepted on this endpoint; REVIEW is available on the workflow configuration key of the same name.
NO_ACTION, DECLINE Response
Check completed — wrong codes and missing verifications also return 200; inspect status, not the HTTP code. phone is populated only on finalized outcomes (Approved/Declined), null on Failed, and absent on Expired or Not Found.
Approved — correct code, no declining risk. Declined — terminal: a declining risk matched or the attempt budget (3) was exhausted. Failed — wrong code, attempts remaining. Expired or Not Found — no pending verification for this number in the last 5 minutes.
Approved, Declined, Failed, Expired or Not Found