Number Verification V2 — operator token flow

Number Verification V2 adds SIM-based authentication using a GSMA TS.43 operator token. Unlike the network-based flow in Number Verification V1, this path does not require the device to be on a cellular data connection at the time of verification. Wi-Fi and other connections are fine, as long as the device can obtain an operator token from the SIM.

Number Verification V2 is served at the /number-verification/v2/verify endpoint. Number Verification V1 is served at the /number-verification/v0/verify endpoint.

This page covers the verify number use case: confirm that the phone number the user entered matches the number on the device. The flow uses OpenID4VP on your backend, the Android Credential Manager on the device, and the Number Verification V2 verify API with the credential field.

When to use this flowheader link

Operator token flow (this page, V2)Network-based flow (V1)
ConnectionWi-Fi, cellular, or otherCellular data on the device (not Wi-Fi or hotspot)
User stepsOS consent dialog via Credential ManagerOpen authorization URL on the device
PlatformAndroid with up-to-date Google Play ServicesAny browser or app that can open the auth URL
Best forNative Android apps where users may be off the home networkWeb or simpler integrations on cellular

If the device cannot produce an operator token (unsupported OS, missing Play Services, user declined consent), fall back to the V1 network-based flow.

Overviewheader link

Your backend drives the flow. The mobile app is a pass-through for the Credential Manager step; it never sees decryption keys or the operator token in cleartext.

Number Verify V2 call flow

  1. The app asks its backend to start verification for a phone number.
  2. The backend builds an incomplete OpenID4VP request and sends it to Network as Code. NaC signs the request and returns a completed DCQL payload.
  3. The app passes that payload to the Android Credential Manager. The user consents in an OS dialog.
  4. The app forwards the encrypted SD-JWT response to its backend.
  5. The backend calls Number Verification V2 /verify with the phone number and the SD-JWT in the credential field. Network as Code decrypts the credential, exchanges the operator token with the operator, and returns the result.

Certificate management, aggregator registration, and operator-token exchange happen inside Network as Code. Your integration only touches the steps above.

Prerequisitesheader link

Step 1 — Build an incomplete OpenID4VP requestheader link

On your backend, construct an OpenID4VP request object that describes the credential you need. Leave credential_authorization_jwt out of the DCQL credential meta object — Network as Code fills that in.

Generate a fresh, cryptographically random nonce for each verification attempt. Store the nonce server-side; you will need it to correlate the Credential Manager response later.

Example incomplete request:

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt",
  "nonce": "1137c18a-1d52-4952-90b5-81f6a36f63e4",
  "state": "your-server-side-session-id",
  "dcql_query": {
    "credentials": [
      {
        "id": "aggregator-nac",
        "format": "dc-authorization+sd-jwt",
        "meta": {
          "vct_values": [
            "number-verification/verify/ts43"
          ]
        },
        "claims": [
          {
            "path": ["subscription_hint"],
            "values": [1]
          },
          {
            "path": ["phone_number_hint"],
            "values": ["+358401234567"]
          }
        ]
      }
    ]
  }
}
FieldDescription
nonceSingle-use random value for this verification attempt.
stateOptional. Echoed back by Network as Code; use it to correlate this verification attempt with a server-side session.
vct_valuesMust be number-verification/verify/ts43. The Network as Code operator token flow currently supports the verify use case only.
phone_number_hintOptional. The E.164 number the user entered, to guide SIM selection in multi-SIM setups.
subscription_hintOptional. Numeric SIM slot index (for example 1 for the first SIM) in multi-SIM devices.

Treat the completed DCQL JSON you receive from Network as Code as an opaque blob when handing it to the client. Do not modify it after NaC returns it.

Step 2 — Complete the request with Network as Codeheader link

Send the incomplete OpenID4VP object to Network as Code. NaC adds the signed credential_authorization_jwt (including encryption keys and consent text for the OS dialog) and returns the completed request.

curl --request POST \
  --url 'https://network-as-code.p-eu.rapidapi.com/oauth2/v1/auth/openid4vp' \
  --header 'Content-Type: application/json' \
  --header 'X-RapidAPI-Host: network-as-code.nokia.rapidapi.com' \
  --header 'X-RapidAPI-Key: YOUR-API-KEY' \
  --data '{
    "response_type": "vp_token",
    "response_mode": "dc_api.jwt",
    "nonce": "1137c18a-1d52-4952-90b5-81f6a36f63e4",
    "state": "your-server-side-session-id",
    "dcql_query": {
      "credentials": [
        {
          "id": "aggregator-nac",
          "format": "dc-authorization+sd-jwt",
          "meta": {
            "vct_values": ["number-verification/verify/ts43"]
          },
          "claims": [
            {
              "path": ["subscription_hint"],
              "values": [1]
            },
            {
              "path": ["phone_number_hint"],
              "values": ["+358401234567"]
            }
          ]
        }
      ]
    }
  }'

This is the same body constructed in Step 1, sent as-is to Network as Code.

A successful response is the same OpenID4VP structure with credential_authorization_jwt populated inside dcql_query.credentials[].meta. Return this JSON to your mobile app.

Step 3 — Obtain the credential on the deviceheader link

Pass the completed OpenID4VP JSON from your backend to the Android app. The app invokes the Android Credential Manager to present the OS consent dialog and retrieve an encrypted verifiable presentation (SD-JWT).

The SD-JWT is encrypted end-to-end between the device and Network as Code. Your app must not attempt to decrypt or parse it — forward the response to your backend as-is.

For platform integration details, required dependencies, and request formatting for Credential Manager, see the upstream guide: Android phone number verification with digital credentials.

At a high level, the Credential Manager response has this shape:

{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "vp_token": {
      "aggregator-nac": ["eyJ...~eyJ..."]
    }
  }
}

Extract the SD-JWT string from data.vp_token (the key matches the id you used in the DCQL credential). Send that string to your backend together with the nonce from step 2.

Step 4 — Verify the phone numberheader link

Call the Number Verification V2 verify endpoint with:

Authenticate the request with your Network as Code API key. Number Verification V2 requires a 3-legged authorization with the number-verification:verify scope. Network as Code performs that authorization on your behalf by exchanging the TS.43 operator token supplied in credential with the operator, so your backend does not need to run a separate authorization code flow for this path.

FieldTypeRequiredDescription
phoneNumberstringone of*E.164 phone number to verify, prefixed with +.
hashedPhoneNumberstringone of*SHA-256 hash (hex-encoded) of the E.164 phone number, including the + prefix.
credentialstringYesSD-JWT from the Android Credential Manager containing the TS.43 operator token.

* Exactly one of phoneNumber or hashedPhoneNumber must be present. Sending both, or neither, returns a 400 INVALID_ARGUMENT error.

curl --request POST \
  --url 'https://network-as-code.p-eu.rapidapi.com/passthrough/camara/v1/number-verification/number-verification/v2/verify' \
  --header 'Content-Type: application/json' \
  --header 'X-RapidAPI-Host: network-as-code.nokia.rapidapi.com' \
  --header 'X-RapidAPI-Key: YOUR-API-KEY' \
  --header 'x-correlator: b4333c46-49c0-4f62-80d7-f0ef930f1c46' \
  --data '{
    "phoneNumber": "+358401234567",
    "credential": "eyJ...~eyJ..."
  }'

Alternatively, verify with a hashed phone number:

curl --request POST \
  --url 'https://network-as-code.p-eu.rapidapi.com/passthrough/camara/v1/number-verification/number-verification/v2/verify' \
  --header 'Content-Type: application/json' \
  --header 'X-RapidAPI-Host: network-as-code.nokia.rapidapi.com' \
  --header 'X-RapidAPI-Key: YOUR-API-KEY' \
  --data '{
    "hashedPhoneNumber": "a32f67ab4e4312618b09cd23ed8ce41b13e095fe52b73b2e8da8ef49830e50db",
    "credential": "eyJ...~eyJ..."
  }'

Verify responseheader link

A successful response:

{
  "devicePhoneNumberVerified": true
}
FieldTypeDescription
devicePhoneNumberVerifiedbooleantrue if the supplied number matches the SIM on the device; false otherwise.

Error responsesheader link

HTTP statusCodeMeaning
400INVALID_ARGUMENTMalformed request, for example both phoneNumber and hashedPhoneNumber supplied, or an invalid E.164 value.
401UNAUTHENTICATEDMissing, invalid, or expired credentials.
403PERMISSION_DENIEDInsufficient permission or scope for Number Verification.
403NUMBER_VERIFICATION.USER_NOT_AUTHENTICATED_BY_MOBILE_NETWORKThe user was not authenticated via the mobile network.
404NOT_FOUNDThe requested resource does not exist.
422UNPROCESSABLE_CONTENTThe request was well-formed but could not be processed, for example an invalid or undecryptable credential.
5xxINTERNAL, NOT_AVAILABLE, TIMEOUTServer-side or upstream operator error. Retry with backoff or fall back to Number Verification V1.

This table lists the most common errors. POST /oauth2/v1/auth/openid4vp (step 2) returns 422 with a detail array of validation errors if the incomplete OpenID4VP request is malformed.

See Number Verification API HTTP responses for full response shapes.

Error handlingheader link

SituationWhat to do
User declines the OS consent dialogTreat as verification cancelled. Offer Number Verification V1 if appropriate.
nonce reused or expiredStart a new flow from step 1 with a fresh nonce and a new OpenID4VP request.
Invalid or undecryptable credentialNetwork as Code returns an error. Do not retry with the same SD-JWT; restart from step 1.
Device lacks Credential Manager supportFall back to Number Verification V1.

Security notesheader link

Last updated September 18, 2026