SMS#
Use the SMS delivery method to send a one-time code in a text message. The user enters the code in your application, and your application reports it to the Verification API.
How it works#
You start a verification with
delivery_methodset tosms.The Verification API sends a text message containing a newly generated numeric code to the destination. The code length is set on the OTP application.
The user enters the code in your application.
Your application reports the
codeto the report endpoint.If the code matches, the verification status becomes
verified.
By default, the message is sent from DIDWW’s shared senders. An OTP application can use dedicated senders from the account’s verified senders, selected in Sender IDs in the SMS Settings section of the application. A sender is verified per destination country. A verification sends from a selected sender that is approved for the destination country, and falls back to DIDWW’s shared senders otherwise. See SMS Settings. To get a sender verified, see Sender ID Verifications.
Starting an SMS verification#
Set delivery_method to sms. Add optional SMS settings in an sms object inside
data. The API reads this object only when delivery_method is sms.
The sms object accepts the following options:
languagesselects the preferred message-template languages. See Supported languages.autofillselects the marker that lets the phone offer or capture the code automatically. See Code autofill.template_groupnames one of your custom SMS template groups. See Custom templates.
The following example uses HTTP Basic authentication and requests the en-US message
template:
http
POST /api/v1/verifications HTTP/1.1
Host: verification.didww.com
Content-Type: application/json
Accept: application/json
Authorization: Basic eW91cl9hcHBfa2V5OnlvdXJfYXBwX3NlY3JldA==
{
"data": {
"destination": "+4915112345678",
"delivery_method": "sms",
"sms": {
"languages": ["en-US"]
}
}
}
curl
curl -i -X POST https://verification.didww.com/api/v1/verifications -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"data": {"delivery_method": "sms", "destination": "+4915112345678", "sms": {"languages": ["en-US"]}}}' --user your_app_key:your_app_secret
response
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"id": "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21",
"destination": "4915112345678",
"delivery_method": "sms",
"fee": "0.06",
"status": "pending",
"error_code": null,
"error_detail": null,
"expires_at": "2026-07-15T10:05:00.000Z",
"sms": {
"template": "Your code is {{CODE}}",
"language": "en-US",
"interception_timeout": 300,
"code_length": 6,
"template_group": null,
"template_group_used": false,
"template_fallback_reason": null
}
}
}
This response shows an approved start with status pending. A 201 Created response
can also contain status denied, for example when a request callback rejects the
verification, the application is paused, or the destination country is blocked, or
status failed when DIDWW cannot deliver SMS to the destination. Always inspect
status and error_code.
A pending status means the verification was accepted for delivery. It does not confirm
that the text message reached the destination.
The response includes an sms object containing SMS-specific information:
templatecontains the message template.{{CODE}}marks where the generated code appears.languageis the language the API selected, which is not necessarily the first one requested. Compare it with the list you sent to detect a fallback toen-US.interception_timeoutspecifies, in seconds, how long an on-device SMS listener should remain active. It equals the verification lifetime set by the OTP application’s Code timeout. It does not control verification expiry. Manual code entry remains available untilexpires_at.code_lengthis the number of digits in the code sent for this verification, from 4 to 8. Use it to size the code input field.autofilldescribes the autofill marker added to the message, withtype(app_hashordomain) andvalue(the app hash or the SMS domain). It is omitted when the message has no marker.app_hashis also returned, for typeapp_hashonly; it is deprecated, so readautofill.valueinstead.template_group,template_group_used, andtemplate_fallback_reasonshow whether a custom template group produced the message. See Custom templates.
Supported languages#
Pass sms.languages as BCP 47 tags, most preferred first. The first supported tag wins;
if none of them is supported, the message is sent in en-US.
Tags are matched exactly, so the region subtag is required: pl does not match
pl-PL and falls back to en-US. A tag that is not a well-formed BCP 47 tag is
rejected with languages_invalid instead.
Read sms.language from the response to see which language was actually used.
The following language tags are supported:
BCP 47 tag |
Language |
|---|---|
|
Bulgarian (Bulgaria) |
|
Bosnian (Bosnia and Herzegovina) |
|
Czech (Czechia) |
|
Danish (Denmark) |
|
German (Germany) |
|
Greek (Greece) |
|
English (United Kingdom) |
|
English (United States) |
|
Spanish (Latin America) |
|
Spanish (Spain) |
|
Estonian (Estonia) |
|
Finnish (Finland) |
|
French (France) |
|
Hebrew (Israel) |
|
Croatian (Croatia) |
|
Hungarian (Hungary) |
|
Icelandic (Iceland) |
|
Italian (Italy) |
|
Japanese (Japan) |
|
Georgian (Georgia) |
|
Lithuanian (Lithuania) |
|
Latvian (Latvia) |
|
Macedonian (North Macedonia) |
|
Malay (Malaysia) |
|
Maltese (Malta) |
|
Norwegian Bokmål (Norway) |
|
Dutch (Netherlands) |
|
Polish (Poland) |
|
Portuguese (Brazil) |
|
Portuguese (Portugal) |
|
Romanian (Romania) |
|
Russian (Russia) |
|
Slovak (Slovakia) |
|
Slovenian (Slovenia) |
|
Albanian (Albania) |
|
Serbian (Serbia) |
|
Swedish (Sweden) |
|
Thai (Thailand) |
|
Ukrainian (Ukraine) |
|
Chinese Simplified (China) |
|
Chinese Traditional (Hong Kong) |
Code autofill#
An autofill marker lets the phone offer the code to the user, or lets your application read
it, so the user does not type it. Choose the marker with sms.autofill.type:
|
Effect |
|---|---|
|
Appends a last line |
|
Formats the message for the Android SMS Retriever API. Pass your application’s hash in
|
|
Adds no marker. The message contains only the template text. |
When you omit autofill, or send null, the OTP application decides: messages get the
domain marker when the application has an iOS SMS domain, and no marker otherwise.
The following example requests the Android marker:
{
"data": {
"destination": "+4915112345678",
"delivery_method": "sms",
"sms": {
"autofill": {
"type": "app_hash",
"value": "FA+9qCX9VSu"
}
}
}
}
value is required for app_hash and must not be sent for domain or none. The
API rejects the request with 422 in the following cases:
autofill_invalid: theautofillobject is malformed, or is sent together with the deprecatedsms.app_hash.app_hash_invalid: the app hash is not exactly 11 characters fromA-Z,a-z,0-9,+, and/.sms_domain_not_configured:typeisdomainbut the OTP application has no iOS SMS domain.
Note
The flat sms.app_hash field is deprecated but still accepted. It is read only when
autofill is omitted, and works like autofill with type app_hash.
Domain-bound SMS#
A domain-bound SMS ends with a line that names your website’s domain and repeats the code.
For an application whose iOS SMS domain is example.com, the message looks like this:
Your code is 123456
@example.com #123456
The phone offers the code only on that domain, and in iOS applications associated with it. This helps protect users from phishing sites that ask them to type a code meant for your site.
To send domain-bound SMS:
Set the iOS SMS domain of the OTP application. See Set the iOS SMS domain.
For iOS applications, add the domain to the application’s Associated Domains as
webcredentials:<domain>, and publish anapple-app-site-associationfile on the domain. DIDWW checks only the domain syntax; ownership is proven by that file.Start verifications as usual. Every SMS of the application gets the domain line by default.
To send a message without the domain line, for example to an iOS application that has no
Associated Domains entry, pass "autofill": {"type": "none"}. A request that uses the
Android app_hash marker never gets the domain line.
Important
The domain line counts toward the message length. With a long domain, a message that fits one SMS segment without the line can be split into several segments.
Android SMS Retriever#
With type app_hash, the message starts with <#> and ends with the 11-character
hash of your Android application, as the SMS Retriever API expects:
<#> Your code is 123456 FA+9qCX9VSu
An Android application that listens through SMS Retriever receives the message without asking
for the SMS permission. Keep the listener active for sms.interception_timeout seconds.
Custom templates#
By default, the message text comes from DIDWW’s system templates. To send your own text, create an SMS template group in the User Panel and get its language versions approved. See Custom templates.
A verification uses the group named in sms.template_group, or, when the request names
none, the application’s default SMS template group.
{
"data": {
"destination": "+4915112345678",
"delivery_method": "sms",
"sms": {
"template_group": "acme-login",
"languages": ["de-DE", "en-US"]
}
}
}
A name that does not match one of your SMS template groups is rejected with 422
template_group_not_found. Any other problem with the group does not fail the request:
the message is sent with the system template, and the response says why.
template_group, template_group_used, and template_fallback_reason in the sms
object report the result. See How the API selects a template. When the group is used, sms.template contains your
text and sms.language the language of the version that was sent.
Reporting the code#
Submit the code the user received. The delivery_method must match the verification.
The following example reports a correct code and returns status verified:
http
PATCH /api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 HTTP/1.1
Host: verification.didww.com
Content-Type: application/json
Accept: application/json
Authorization: Basic eW91cl9hcHBfa2V5OnlvdXJfYXBwX3NlY3JldA==
{
"data": {
"delivery_method": "sms",
"code": "123456"
}
}
curl
curl -i -X PATCH https://verification.didww.com/api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"data": {"code": "123456", "delivery_method": "sms"}}' --user your_app_key:your_app_secret
response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21",
"destination": "4915112345678",
"delivery_method": "sms",
"fee": "0.06",
"status": "verified",
"error_code": null,
"error_detail": null,
"expires_at": "2026-07-15T10:05:00.000Z",
"sms": {
"template": "Your code is {{CODE}}",
"language": "en-US",
"interception_timeout": 300,
"code_length": 6,
"template_group": null,
"template_group_used": false,
"template_fallback_reason": null
}
}
}
The response above shows a successful report. The API returns 200 OK with status
verified when the submitted code matches.
If the submitted code is incorrect, the API returns the following response:
HTTP/1.1 422 Unprocessable Content
Content-Type: application/json
{
"errors": [
{
"code": "code_invalid",
"detail": "code is invalid"
}
]
}
After an incorrect report, the verification remains pending, and the user can submit
another code until the attempt limit is reached. After three unsuccessful reports, the
verification becomes failed with error_code too_many_attempts.
Next steps#
Start a verification: Review all start-request and response fields.
Report a verification: Review all report-request and response fields.
Custom templates: Send your own message text.