curl -X POST https://verification.didit.me/v3/email/check/ \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"email": "alice@example.com",
"code": "123456",
"disposable_email_action": "DECLINE"
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.email.check_code(
email="alice@example.com",
code="123456",
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.email.checkCode({
email: "alice@example.com",
code: "123456",
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Email.CheckCode(ctx, didit.EmailCheckCodeParams{
Email: "alice@example.com",
Code: "123456",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import me.didit.Didit;
import me.didit.types.EmailCheckCodeParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.email().checkCode(EmailCheckCodeParams.builder()
.email("alice@example.com")
.code("123456")
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->email->checkCode([
'email' => 'alice@example.com',
'code' => '123456',
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.email.check_code(
email: "alice@example.com",
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.Email.CheckCodeAsync(new EmailCheckCodeParams
{
Email = "alice@example.com",
Code = "123456",
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
"status": "Approved",
"message": "The verification code is correct.",
"email": {
"status": "Approved",
"email": "alice@example.com",
"is_breached": false,
"breaches": [],
"is_disposable": false,
"is_undeliverable": false,
"verification_attempts": 1,
"verified_at": "2026-06-12T01:24:47.311323Z",
"warnings": [],
"lifecycle": [
{
"type": "EMAIL_VERIFICATION_MESSAGE_SENT",
"timestamp": "2026-06-12T01:23:39.580554+00:00",
"details": {
"status": "Success",
"reason": null
},
"fee": 0.02
},
{
"type": "VALID_CODE_ENTERED",
"timestamp": "2026-06-12T01:24:47.311201+00:00",
"details": {
"code_tried": "123456",
"status": "Approved"
},
"fee": 0
},
{
"type": "EMAIL_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 Email OTP API: Validate Email Code
POST /v3/email/check/ to validate a one-time email code with breach, disposable-address, and deliverability signals. Pay-per-use pricing, no contracts.
curl -X POST https://verification.didit.me/v3/email/check/ \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"email": "alice@example.com",
"code": "123456",
"disposable_email_action": "DECLINE"
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.email.check_code(
email="alice@example.com",
code="123456",
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.email.checkCode({
email: "alice@example.com",
code: "123456",
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Email.CheckCode(ctx, didit.EmailCheckCodeParams{
Email: "alice@example.com",
Code: "123456",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import me.didit.Didit;
import me.didit.types.EmailCheckCodeParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.email().checkCode(EmailCheckCodeParams.builder()
.email("alice@example.com")
.code("123456")
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->email->checkCode([
'email' => 'alice@example.com',
'code' => '123456',
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.email.check_code(
email: "alice@example.com",
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.Email.CheckCodeAsync(new EmailCheckCodeParams
{
Email = "alice@example.com",
Code = "123456",
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
"status": "Approved",
"message": "The verification code is correct.",
"email": {
"status": "Approved",
"email": "alice@example.com",
"is_breached": false,
"breaches": [],
"is_disposable": false,
"is_undeliverable": false,
"verification_attempts": 1,
"verified_at": "2026-06-12T01:24:47.311323Z",
"warnings": [],
"lifecycle": [
{
"type": "EMAIL_VERIFICATION_MESSAGE_SENT",
"timestamp": "2026-06-12T01:23:39.580554+00:00",
"details": {
"status": "Success",
"reason": null
},
"fee": 0.02
},
{
"type": "VALID_CODE_ENTERED",
"timestamp": "2026-06-12T01:24:47.311201+00:00",
"details": {
"code_tried": "123456",
"status": "Approved"
},
"fee": 0
},
{
"type": "EMAIL_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"
}Intelligence add-on
Setenable_enrichment to true to also run email intelligence on the address: a risk score and its breach history.
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.
Three 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 |
|---|---|
email_intelligence_score_action | The risk score is above the threshold. |
recent_email_breach_action | The address appears in a recent known data breach. |
frequent_email_breach_action | The address appears in many known data breaches. |
NO_ACTION, the signal is still returned in email.email_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.
Headers
Optional retry-safety key you choose for one operation, up to 255 characters (a UUID works). A request repeated with the same key, endpoint, query and body is answered with the first response, same status and same body, and runs and bills nothing a second time. The same key with another endpoint, query or body returns 409 idempotency_key_reused, and while the first request is still running, 409 idempotency_key_in_progress with Retry-After. A failed request (any 4xx or 5xx) is not stored, so you can send it again with the same key. Keys belong to your application, whether you call with an API key or an access token. See Retries and idempotency.
1 - 255Body
The same email address used in the matching POST /v3/email/send/ call. This is what links the check to the send.
"alice@example.com"
What to do when the same address was already used and approved by a different user (only previously Approved verifications count) (different vendor_data) of your application. DECLINE flips the final status to Declined; NO_ACTION records the risk in email.warnings and fills email.matches.
NO_ACTION, DECLINE When true, also run email intelligence on the address: a risk score and its breach history. Billed as one email_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 email 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 social footprint check finds the address 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. email 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 address in the last 5 minutes.
Approved, Declined, Failed, Expired or Not Found