Asynchronous Implementation

An asynchronous implementation is very similar to the synchronous method; instead of using the device client to make a series of calls, every part of the workflow is included in webhook callbacks sent to a URL you define.

This guide explains how to implement silent authentication using the asynchronous approach, where your backend sets up a callback to receive the authentication result.

VonageApp BackendMobile AppVonageApp BackendMobile AppTrigger VerificationSend Check URLCheck Verification CodeVerify phone numberPOST v2/verifyWebhook 202 OK (check_url, request_id) / ErrorResponse (check_url)GET check_urlSeveral 302 RedirectsHTTP 200 (request_id, code)Request (request_id, code)POST v2/verify/:request_id (code)HTTP 200 (status: completed)Response (Completed)

To use Asynchronous Silent Authentication, you must use JWT Authentication in your requests otherwise you will not receive the necessary webhooks to implement it.

Create an Application

To begin, go to the developer dashboard to create your application:

  • Ensure that Verify is enabled under 'Capabilities'.
  • Configure the Status URL to receive callback events.
  • Generate a JWT using the Application ID and private key of the application.

Trigger Verification

To start the silent authentication process, make a request to /verify. In the following example, the workflow specifies that Verify will first attempt to use Silent Authentication. If the request fails for any reason, it will then fall back to email OTP.

To run the example, replace the following variables in the sample code with your own values:

Variable Description
JWT Authenticates the API request using JWT.
VERIFY_BRAND_NAME The name of your company or service, shown to the user in the verification message.
VONAGE_APPLICATION_PRIVATE_KEY_PATH Path to the private key of your application.
VONAGE_APPLICATION_ID Application ID of your application.
VERIFY_NUMBER The phone number to send the OTP to, in E.164 format (e.g., +441112233447).
VERIFY_TO_EMAIL The email to send the OTP to.

Write the code

Add the following to request.sh:

curl -X POST "https://api.nexmo.com/v2/verify" \
  -H "Authorization: Bearer $JWT"\
  -H 'Content-Type: application/json' \
  -d $'{
	 "brand": "'$VERIFY_BRAND_NAME'",
   "workflow": [
      {
         "channel": "silent_auth",
         "to": "'$VERIFY_NUMBER'"
      },
			{
         "channel": "email",
         "to": "'$VERIFY_EMAIL_TO'"
      }
   ]
}'

View full source

Run your code

Save this file to your machine and run it:

sh request.sh

Response

If the request is successful, the response will return HTTP 200 with the check_url in the body, which we’ll need in the next step:

HTTP/1.1 200 Ok 
{
  "request_id": "470b478f-334c-4f6f-b90d-b44e77ed24bf",
  "check_url": "https://api-eu-4.vonage.com/v2/verify/470b478f-334c-4f6f-aaab-b4a342aed24bf/silent-auth/redirect"
}

If there is an error with the request, such as an incorrectly formatted phone number, the response will contain an HTTP 422 error like this:

HTTP/1.1 422 Unprocessable Entity 
{
  "title": "Invalid params",
  "detail": "The value of one or more parameters is invalid",
  "instance": "b30db5a7-338e-402e-aa5a-40073a9aa07c",
  "type": "https://developer.vonage.com/en/api-errors#invalid-params",
  "invalid_parameters": [
    {
      "name": "workflow[0]",
      "reason": "`to` Phone number is invalid"
    }
  ]
}

Since we are using the asynchronous implementation, in parallel with the API response, you will receive a callback (or webhook) to the URL specified in your dashboard application settings, informing you of the progress of the request.

During the API call to /verify, the Verify API performs internal pre-checks. If everything is in order, the callback will contain the following body:

HTTP/1.1 200 Ok 
{
  "request_id": "21a425df-04b2-43f2-990e-b19ee22e18a0",
  "triggered_at": "2025-07-14T17:01:47.032Z",
  "channel": "silent_auth",
  "status": "action_pending",
  "action": {
    "type": "check",
    "check_url": "https://api-eu-4.vonage.com/v2/verify/21a425df-04b2-43f2-990e-b19ee22e18a0/silent-auth/redirect"
  },
  "type": "event"
}

Until the request expires or is cancelled, check_url will be used to perform a Silent Authentication check by sending the Auth URL. Upon receiving this callback, you must make a

GET
request to the check_url from the mobile device you are trying to authenticate.

In order to verify the user, the

GET
request must be made over a mobile data connection. See Bypass Wi-Fi for Silent Authentication for information on how to force a mobile connection.

If any of these pre-checks fail, for example, due to a network error or an unsupported operator, the callback will look like this:

HTTP/1.1 200 Ok 
{
  request_id: "470b478f-334c-4f6f-b90d-b44e77ed24bf",
  triggered_at: "2025-07-14T16:53:16.965Z",
  channel: "silent_auth",
  status: "failed",
  type: "event"
}

If your /verify request includes a fallback channel, Verify will continue using that channel as shown in the following sequence diagram:

VonageApp BackendMobile AppVonageApp BackendMobile AppTrigger VerificationVerify phone numberPOST v2/verifyInternal coverage checkWebhook 200 (status: failed)fallback channel

Send Auth URL

Once you've made the

GET
request, you'll receive one or more HTTP 302 redirects depending on the territory and carrier of the target device:

HTTP/1.1 302 Found
Location: https://eu.api.silentauth.com/phone_check/v0.2/checks/31eaf23d-b2db-4c42-9d1d-e847e75ab330/redirect

Following the redirects will result in either a HTTP 200 or HTTP 4xx response depending on the outcome. If there is a problem with the network, you might see a response like this:

HTTP/1.1 412 Precondition Failed
Content-Type: application/json

{
  "title": "Network not supported",
  "detail": "Device number does not resolve to a supported Mobile Network Operator.",
  "instance": "78e23b55-1633-465e-9325-6abcf186dd00",
  "type": "https://developer.vonage.com/en/api-errors/verify-v2#precondition-failed"
}

A full list of potential error codes can be found in the API Specification.

The Timeout Management section of the Best Practices guide contains useful information for handling situations like network signal or coverage issues during this step.

If the request is valid, you will receive an HTTP 200 response containing your request_id and a code:

{
  "request_id": "c11236f4-00bf-4b89-84ba-88b25df97315",
  "code": "si9sfG"
}

Note: To ensure a secure authentication check and mitigate against a potential man in the middle attack, store the original request_id and compare it with the request_id returned in the response. If the IDs don't match, the silent authentication check should be aborted. See our sample application for an example implementation of how to mitigate against the attack.

Check the Verification Code

Once the end-user receives the code, your client application must send a

POST
request to the /v2/verify/{request_id} endpoint, replacing {request_id} with the ID you received in the previous call.

To run the example, replace the following variables in the sample code with your own values:

Variable Description
JWT Authenticates the API request using JWT.
VERIFY_REQUEST_ID The request_id received in the previous step.
VONAGE_APPLICATION_PRIVATE_KEY_PATH Private key of your application.
VONAGE_APPLICATION_ID Application ID of your application.
VERIFY_CODE The verification code received by the end-user

Write the code

Add the following to check-verification-code.sh:

curl -X POST "https://api.nexmo.com/v2/verify/$VERIFY_REQUEST_ID" \
  -H "Authorization: Bearer $JWT"\
  -H 'Content-Type: application/json' \
  -d $'{
    "code": "'$VERIFY_CODE'"
  }'

View full source

Run your code

Save this file to your machine and run it:

sh check-verification-code.sh

Note: a code for a silent authentication workflow can only be checked once.

If the code is valid, you will receive a callback with the status completed:

{
   "request_id": "31eaf23d-b2db-4c42-9d1d-e847e75ab330",
   "status": "completed"
}

Or, if there is an error, you will see 'Invalid Code':

{
   "title": "Invalid Code",
   "type": "https://www.developer.vonage.com/api-errors/verify#invalid-code",
   "detail": "The code you provided does not match the expected value.",
   "instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf"
}

You will then receive a final callback with the result of the check. If successful, you will see a callback with "status": "completed":

{
    "request_id": "c11236f4-00bf-4b89-84ba-88b25df97315",
    "triggered_at": "2020-01-01T14:00:00.000Z",
    "type": "event",
    "channel": "silent_auth",
    "status": "completed",
}

If unsuccessful, for example if the user has been rejected, this will be indicated in the status field:

{
    "request_id": "c11236f4-00bf-4b89-84ba-88b25df97315",
    "triggered_at": "2020-01-01T14:00:00.000Z",
    "type": "event",
    "channel": "silent_auth",
    "status": "user_rejected",
}

At this point, your silent authentication verification is complete.