Start a verification#

Create a verification for a destination phone number and select how the verification challenge is delivered. The response contains the verification identifier, initial status, expiration time, and delivery-method details when applicable.

For an approved request, the Verification API attempts to deliver the challenge by SMS or phone call.

Request#

HTTP method: POST

Path: /api/v1/verifications

Request body#

The request body contains a top-level data object. The following table lists the fields accepted inside data and identifies which fields are required:

Field

Type

Required

Description

destination

string

Yes

Phone number to verify in E.164 format. The leading + is optional.

delivery_method

string

Yes

The method used to deliver the verification challenge. Supported values are sms and callout.

sms

object

No

Options for the sms delivery method. Send this object only when delivery_method is sms. The API ignores it when another delivery method is selected.

sms.languages

Array of string

No

Preferred message-template languages as BCP 47 tags, ordered from most to least preferred. Tags are matched exactly, so pl does not match pl-PL. If none of the requested languages has a template, the API uses en-US. Invalid tags return languages_invalid. See Supported languages.

sms.app_hash

string

No

Android SMS Retriever application hash. It must contain exactly 11 characters from A-Z, a-z, 0-9, +, and /. When supplied, the SMS message starts with <#> and ends with the application hash so a compatible Android application can capture the code automatically. Omit this field on other platforms.

callout

object

No

Options for the callout delivery method. Send this object only when delivery_method is callout. The API ignores it when another delivery method is selected.

callout.languages

Array of string

No

Preferred announcement languages as BCP 47 tags, ordered from most to least preferred. These are the same tags with the same semantics as sms.languages, so one language list works for both delivery methods. Tags are matched exactly, so pt does not match pt-PT. If none of the requested languages has a recording, the announcement uses en-US. Invalid tags return languages_invalid. See Supported languages.

Response#

The endpoint returns JSON responses. When a verification is created, its verification object appears under a top-level data key. Errors that prevent a verification from being created appear under a top-level errors array.

The following table lists the HTTP status codes returned by this endpoint:

Status

Meaning

201 Created

The verification record was created. Inspect status and error_code to determine whether delivery was approved or denied.

401 Unauthorized

Authentication failed, credentials are missing or invalid, or the authentication mode is below the minimum configured for the OTP application.

402 Payment Required

The account balance is insufficient to start a verification.

422 Unprocessable Content

Request validation failed. This includes an invalid destination, unsupported delivery method, or invalid delivery-method options.

See Errors and status codes for the error response structure and available error codes.

Note

A 201 Created response confirms that the verification record was created. It does not confirm that the challenge was delivered. An approved start normally has status pending, and delivery can still fail later.

A request callback can deny the start before delivery. A denied start can also return 201 Created with status denied and a matching error_code. Always inspect both fields.

Examples#

The REST API examples use HTTP Basic authentication. See REST API authentication for credential and header requirements.

The SDK examples assume that the corresponding SDK is installed and initialized. See the Ruby SDK, iOS SDK, and Android SDK.

The identifiers, expiration times, and fees shown in the examples are illustrative.

REST API#

Send a POST request to create a verification. When the record is created, the response returns a verification object containing its initial state under a top-level data key.

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:02:00.000Z",
    "sms": {
      "template": "Your code is {{CODE}}",
      "language": "en-US",
      "interception_timeout": 120
    }
  }
}

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": "callout",
    "callout": {
      "languages": ["de-DE"]
    }
  }
}

curl

curl -i -X POST https://verification.didww.com/api/v1/verifications -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"data": {"callout": {"languages": ["de-DE"]}, "delivery_method": "callout", "destination": "+4915112345678"}}' --user your_app_key:your_app_secret

response

HTTP/1.1 201 Created
Content-Type: application/json

{
  "data": {
    "id": "2b3c4d5e-6f70-4b3c-9d0e-1f2a3b4c5d6e",
    "destination": "4915112345678",
    "delivery_method": "callout",
    "fee": "0.08",
    "status": "pending",
    "error_code": null,
    "error_detail": null,
    "expires_at": "2026-07-15T10:02:00.000Z",
    "callout": {
      "language": "de-DE"
    }
  }
}

Ruby SDK#

The Ruby SDK sends the request when start_verification(...) is called and returns a verification object containing the initial state.

verification = client.start_verification(
  destination:     "+4915112345678",
  delivery_method: "sms",
  sms:             {languages: ["en-US"]}
)

verification.id           # => "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21"
verification.status       # => "pending"
verification.pending?     # => true
verification.sms_language # => "en-US"
verification = client.start_verification(
  destination:     "+4915112345678",
  delivery_method: "callout",
  callout:         {languages: ["de-DE"]}
)

verification.id               # => "2b3c4d5e-6f70-4b3c-9d0e-1f2a3b4c5d6e"
verification.status           # => "pending"
verification.callout_language # => "de-DE"

iOS SDK#

The iOS SDK sends the request when start(...) is called and returns a Verification containing the initial state.

let verification = try await client.start(
    destination: "+4915112345678",
    method: .sms,
    sms: .init(languages: ["en-US"])
)

verification.id     // "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21"
verification.status // .pending
let verification = try await client.start(
    destination: "+4915112345678",
    method: .callout,
    callout: .init(languages: ["de-DE"])
)

verification.id     // "2b3c4d5e-6f70-4b3c-9d0e-1f2a3b4c5d6e"
verification.status // .pending

Inspect verification.status before asking the user for a code. A denied start returns a Verification with status .denied instead of throwing an HTTP error.

Android SDK#

The Android SDK returns a VerificationHandle. Calling start(...) does not send the request. Collect handle.states exactly once to send the request and receive verification state changes. After VerificationState.Starting, an approved start emits VerificationState.AwaitingInput, which corresponds to the API status pending.

val handle = didww.start(
    destination = "+4915112345678",
    method = DeliveryMethod.SMS,
    sms = SmsOptions(languages = listOf("en-US")),
)
val handle = didww.start(
    destination = "+4915112345678",
    method = DeliveryMethod.CALLOUT,
    callout = CalloutOptions(languages = listOf("de-DE")),
)

Collect the returned handle from a ViewModel-scoped coroutine:

viewModelScope.launch {
    handle.states.collect { state ->
        when (state) {
            is VerificationState.AwaitingInput ->
                println("Verification ${state.verificationId} is pending")
            is VerificationState.Denied ->
                println(state.error?.detail ?: "Verification denied")
            is VerificationState.SetupError ->
                println("Configuration error: ${state.code}")
            is VerificationState.Failed ->
                println("Start failed: ${state.reason}")
            else -> Unit
        }
    }
}