Getting Started#

This guide walks you through creating an OTP application, generating API credentials, and running your first verification from start to finish. The walkthrough uses the sandbox environment so you can test the integration before moving it to production.


Choose an environment#

Create the OTP application and credentials in the same environment as the Verification API endpoint you plan to use:

Environment

User Panel

Verification API base URL

Use

Sandbox

DIDWW Sandbox User Panel

https://verification-sandbox.didww.com

Integration testing

Production

DIDWW Production User Panel

https://verification.didww.com

Live verifications

Important

Credentials are environment-specific. Use credentials from a sandbox OTP application with the sandbox API, and credentials from a production OTP application with the production API.


Before you begin#

  • An account in the selected environment is required. For testing, open the DIDWW Sandbox User Panel. For production, sign in to the DIDWW Production User Panel or create a DIDWW account.

  • Access to the corresponding User Panel is required to create an OTP application and copy its credentials.

  • A positive account balance is required for production. The verification fee is billed when a verification reaches verified. SMS and call delivery costs are billed separately.

  • A phone number you control is required when testing delivery through the production environment.


Step 1: Create an OTP application#

An OTP application groups the credentials and settings used by your verification requests. You need one before you can start verifications, because each request is tied to an OTP application key, secret, callback URL, and minimum authentication mode.

For this walkthrough, create the application in the DIDWW Sandbox User Panel.

  1. Open the User Panel for your selected environment: use the DIDWW Sandbox User Panel for testing or the DIDWW Production User Panel for live verifications.

  2. Expand OTP Verify.

  3. Select Applications.

  4. Click Create application.

  5. Enter a name for the application.

  6. Optionally, enter a description.

  7. Set a callback URL if you plan to use public authentication, including the iOS and Android SDK examples in this guide. The Verification API calls this URL so your backend can approve or reject each start request. The callback URL is optional for basic and application authentication. See Callbacks.

  8. Select the minimum authentication mode required for requests to this OTP application:

    • Public: anyone with the credential key can call the OTP endpoint.

    • Basic: callers must use HTTP Basic authentication with the credential key as the username and the credential secret as the password.

    • Application: callers must sign each request with the credential secret.

  9. Click Create to save the application.

Create OTP application form in the DIDWW Sandbox User Panel

Create an OTP application in the DIDWW Sandbox User Panel.#

Step 2: Get your API credentials#

Each OTP application has a key and a secret. The key identifies the OTP application when you make verification requests. The secret is used for stronger authentication modes, such as HTTP Basic or signed application authentication.

Copy the credentials from the OTP application in your selected environment. This walkthrough uses credentials created in the DIDWW Sandbox User Panel.

The REST API and Ruby SDK examples use both credentials with basic authentication. The iOS and Android SDK examples use only the key with public authentication.

  1. In OTP Verify > Applications, find the application you created.

  2. In the Credentials column, copy the key and secret for that application. Use the copy icon to copy each value without revealing it on screen.

  3. If you need to view the actual value before copying it, click the eye icon.

Warning

Treat the secret like a password. Store it securely and never expose it in client-side code or public repositories. If it is leaked, rotate it from the corresponding DIDWW User Panel.

OTP application credentials in the DIDWW Sandbox User Panel

Copy the key and secret from the Credentials column.#

Step 3: Start a verification#

Call the start endpoint with the destination number and delivery method. The examples use the sandbox environment and send an SMS code.

Note

The REST API and Ruby SDK examples use basic authentication. The iOS and Android SDK examples use public authentication, which requires the callback URL configured in Step 1. An OTP application with minimum auth mode public accepts both authentication modes. All examples use credentials created in the DIDWW Sandbox User Panel.

http

POST /api/v1/verifications HTTP/1.1
Host: verification-sandbox.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-sandbox.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
    }
  }
}

Note

The Authorization header contains the Base64 encoding of key:secret. Most HTTP clients build this header when you supply the key as the username and the secret as the password.

Install the Ruby SDK, then create a sandbox client and start the verification:

require "didww/otp_verification"

client = DIDWW::OTPVerification::Client.new(
  key:    "your-app-key",
  secret: "your-app-secret",
  env:    :sandbox
)

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

Install the iOS SDK, then create a sandbox client with public authentication and start the verification:

import DIDWWVerification

let client = VerificationClient(
    environment: .sandbox,
    auth: .public(appKey: "your-app-key")
)

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

Install the Android SDK. Inside an AndroidViewModel, create a sandbox client with public authentication and start collecting the verification states. Calling start(...) creates a handle. The request is sent when handle.states is first collected.

import androidx.lifecycle.viewModelScope
import com.didww.android.sdk.verification.Auth
import com.didww.android.sdk.verification.DeliveryMethod
import com.didww.android.sdk.verification.Environment
import com.didww.android.sdk.verification.SmsOptions
import com.didww.android.sdk.verification.VerificationState
import com.didww.android.sdk.verification.all.DidwwVerification
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch

val didww = DidwwVerification(
    context = application,
    auth = Auth.Public("your-app-key"),
    environment = Environment.Sandbox,
)

val verificationState = MutableStateFlow<VerificationState>(
    VerificationState.Starting,
)

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

viewModelScope.launch {
    handle.states.collect { verificationState.value = it }
}

Keep the returned id, verification, or handle, depending on your integration. You need it to report the code and read the verification status in the next steps.

Step 4: Report the code#

When the user enters the code they received, submit it to the report endpoint:

http

PATCH /api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 HTTP/1.1
Host: verification-sandbox.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-sandbox.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:02:00.000Z",
    "sms": {
      "template": "Your code is {{CODE}}",
      "language": "en-US",
      "interception_timeout": 120
    }
  }
}
verification = client.report_verification(
  "0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21",
  delivery_method: "sms",
  code:            "123456"
)

verification.status    # => "verified"
verification.verified? # => true
let result = try await client.verify(
    verification,
    code: "123456"
)

result.status // .verified

Submit the code through the handle created in Step 3. The existing state collector receives the result.

handle.submit("123456")

A successful submission emits VerificationState.Submitting followed by VerificationState.Verified. If the code is rejected, the flow returns to VerificationState.AwaitingInput with lastError set.

A status of verified confirms the user controls the number. If the code is incorrect, the report is rejected and the verification remains available for another attempt until the attempt limit is reached. See Errors and status codes for the exact limit and other status values.

Step 5: Check the status (optional)#

Read the current status of a verification at any time. REST, Ruby, and iOS make a status request. Android reads the latest state received by the collector created in Step 3.

http

GET /api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 HTTP/1.1
Host: verification-sandbox.didww.com
Accept: application/json
Authorization: Basic eW91cl9hcHBfa2V5OnlvdXJfYXBwX3NlY3JldA==

curl

curl -i -X GET https://verification-sandbox.didww.com/api/v1/verifications/0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21 -H "Accept: application/json" --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:02:00.000Z",
    "sms": {
      "template": "Your code is {{CODE}}",
      "language": "en-US",
      "interception_timeout": 120
    }
  }
}
verification = client.get_verification("0f9c8b7a-1e2d-4c3b-9a8f-7e6d5c4b3a21")

verification.status    # => "verified"
verification.verified? # => true
let current = try await client.status(verification)

current.status // .verified

Do not collect handle.states a second time. Read the latest value mirrored by the collector created in Step 3:

when (val current = verificationState.value) {
    is VerificationState.AwaitingInput -> println("pending")
    is VerificationState.Verified -> println("verified")
    is VerificationState.Denied -> println("denied")
    is VerificationState.Failed -> println("failed")
    is VerificationState.SetupError -> println("configuration error")
    VerificationState.Expired -> println("expired")
    else -> println("in progress")
}

Move to production#

After the sandbox flow works as expected, configure the integration separately for production:

  1. Sign in to the DIDWW Production User Panel.

  2. Create a new OTP application in the production account and configure its callback URL and minimum authentication mode.

  3. Copy the key and secret from the production OTP application. Store them separately from the sandbox credentials.

  4. Change the API or SDK environment from sandbox to production:

    Integration

    Sandbox

    Production

    REST API

    https://verification-sandbox.didww.com

    https://verification.didww.com

    Ruby SDK

    env: :sandbox

    env: :production or omit env

    iOS SDK

    environment: .sandbox

    environment: .production

    Android SDK

    environment = Environment.Sandbox

    environment = Environment.Production

  5. Run a controlled verification using a phone number you can access.

Warning

Production requests can contact real destinations and incur verification and delivery charges. Confirm that the production OTP application, credentials, callback URL, and API environment are configured together before sending requests.

Next steps#