Skip to content

OTP Verification

The OTP Verification integration verifies consumer phone ownership by sending a 6-digit one-time passcode via SMS. The consumer enters the code on your form, and eConsent confirms the match, providing cryptographic proof that the phone number belongs to the person who submitted consent.

DetailValue
Cost$0.10 per verification
Rate limit300 requests/min per company
Cooldown2 minutes per phone number
BillingWallet-based (pre-paid)

OTP verification uses a two-step flow:

  1. Start. Your application sends the consumer’s phone number to the start endpoint. eConsent sends a 6-digit code via SMS.
  2. Complete. The consumer enters the code on your form. Your application sends the code to the complete endpoint for verification.

OTP verification two-step flow diagram

  1. Go to Integrations in your dashboard at app.econsent.org.
  2. Enable OTP Verification and copy your API token.
  3. Send a verification code:
Terminal window
curl -X POST https://api.econsent.org/api/integrations/YOUR_COMPANY_ID/otp/start \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"phone_number": "5551234567",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}'

Sends a 6-digit OTP code to the consumer’s phone via SMS.

POST https://api.econsent.org/api/integrations/:companyId/otp/start
ParameterTypeDescription
companyIdstringYour Company ID
FieldTypeRequiredDescription
phone_numberstringYesConsumer’s phone number (digits only, no country code prefix required)
session_idstringYesThe eConsent session ID to associate this verification with
{
"phone_number": "5551234567",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

The response includes carrier information detected from the phone number, along with billing details showing the charge deducted from your wallet.

{
"success": true,
"status": "code_sent",
"carrier_name": "T-Mobile",
"carrier_type": "WIRELESS",
"phone": "5551234567",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"message": "Verification code sent",
"billing": {
"cost_charged": 0.10,
"remaining_balance": 99.90
},
"processing_time_ms": 450
}

Verifies the code entered by the consumer against the pending OTP.

POST https://api.econsent.org/api/integrations/:companyId/otp/complete
ParameterTypeDescription
companyIdstringYour Company ID
FieldTypeRequiredDescription
phone_numberstringYesThe same phone number used in the start request
codestringYesThe 6-digit code entered by the consumer
session_idstringYesThe eConsent session ID from the start request
{
"phone_number": "5551234567",
"code": "123456",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

When the code matches, the response includes identity metadata associated with the phone number. If a session_id was provided, the verification result is automatically stored on that session for inclusion in the consent certificate.

{
"success": true,
"status": "verified",
"phone": "5551234567",
"verified_at": "2026-04-02T14:31:12Z",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identity_meta": {
"first_name": "Sarah",
"last_name": "Mitchell",
"city": "Richmond",
"state": "VA"
}
}

If no pending verification exists (expired or never started), the response indicates the failure:

{
"success": false,
"message": "No pending verification for this phone number."
}

Use Bearer token authentication with your OTP Verification integration token:

Authorization: Bearer YOUR_API_TOKEN
LimitValueDescription
Per company300 requests/minApplies across all OTP requests (start + complete)
Per phone number1 request per 2 minutesCooldown on start requests to prevent SMS spam

When OTP verification completes successfully, the result is automatically attached to the consent certificate’s Verification Dossier. The dossier includes:

FieldDescription
Phone numberThe verified phone number
Verification statusverified or failed
Verified atTimestamp of successful verification
Session IDThe session the verification is linked to

Verification Dossier showing OTP result on a consent certificate

This provides tamper-proof evidence that the consumer who submitted consent also had physical access to the phone number at the time of consent capture.

StatusErrorDescription
400invalid_phone_numberPhone number format is invalid or missing
400invalid_codeThe code does not match the pending verification
401unauthorizedInvalid or missing API token
402insufficient_balanceWallet balance too low to cover the $0.10 charge
404verification_not_foundNo pending verification exists for this phone number and session
410code_expiredThe verification code has expired (codes are valid for 5 minutes)
429rate_limitedExceeded 300 requests/min or 2-minute per-number cooldown
{
"success": false,
"status": 410,
"error": "code_expired",
"message": "The verification code has expired. Please request a new code.",
"company_id": "comp-abc",
"timestamp": "2026-04-02T14:38:00Z"
}
const companyId = process.env.ECONSENT_COMPANY_ID;
const token = process.env.ECONSENT_OTP_TOKEN;
const baseUrl = `https://api.econsent.org/api/integrations/${companyId}/otp`;
async function startOtp(phoneNumber, sessionId) {
const response = await fetch(`${baseUrl}/start`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({
phone_number: phoneNumber,
session_id: sessionId,
}),
});
const data = await response.json();
if (!data.success) {
throw new Error(data.message);
}
return data;
}
async function completeOtp(phoneNumber, code, sessionId) {
const response = await fetch(`${baseUrl}/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({
phone_number: phoneNumber,
code: code,
session_id: sessionId,
}),
});
const data = await response.json();
if (!data.success) {
throw new Error(data.message);
}
console.log('Verified at:', data.verified_at);
return data;
}
  • Link to session. Always pass the session_id so the verification result is attached to the correct consent certificate.
  • Handle expiration gracefully. Codes expire after 5 minutes. Display a “Resend code” button that respects the 2-minute cooldown.
  • Validate phone first. Use Phone Validation before sending OTP to confirm the number is a reachable mobile line. This avoids wasting $0.10 on undeliverable messages.
  • Client-side UX. Show a countdown timer for the 2-minute cooldown and auto-focus the code input field after sending.
  • Do not retry on invalid code. If the consumer enters the wrong code, let them re-enter it. Do not automatically trigger a new start request.
  • Cost per verification: $0.10 (charged on start)
  • Rate limit: 300 requests/min per company
  • Cooldown: 2 minutes per phone number
  • Billing method: Wallet-based (pre-paid via Stripe)

View all pricing details