Number Verification V1

Number verification confirms that the device uses the phone number provided by the user. When a user enters a phone number on a web page or a device-based app, the backend client makes a request to NaC, which turns to the operator to confirm. Number Verification V1 uses network based authentication.

Number Verification V1 is served at the /number-verification/v0/verify endpoint. Number Verification V2 is served at the /number-verification/v2/verify endpoint; see Number Verification V2 — operator token flow.

The number verification API uses a 3-legged OAuth 2.0 flow, where a network subscriber (a device end-user) gives consent through a Network Provider interface. In this process, a mobile app initiates a phone number verification by requesting and receiving an authorization URL. This URL must be accessed via a mobile connection and involves several redirects before a successful response is passed from the Network Provider and Network as Code. This page describes the steps required to gain authorization and successfully execute the consent flow.

Using the Number Verification API requires explicit end‑user consent. Consent may be captured in one of the following ways:

  1. Directly by the Telecom Operator, or
  2. By the Application Service Provider (Enterprise), when the Telecom Operator delegates this responsibility.

The applicable approach depends on country‑specific regulations and the consent model adopted by the Telecom Operator. When end‑user consent collection is delegated to the Application Service Provider, your application must implement the following:

Consent collection UI

Your application must present a consent screen that complies with the consent collection requirements published by Network as Code:

This document provides reference UI examples and specifies the exact consent language required for each country.

Consent evidence submission

You must capture screenshots of the consent collection UI implemented in your application and upload them as a single PDF during the application registration process.

These screenshots will be reviewed and approved by the relevant Telecom Operators as part of the Application registration process.

When an enterprise is onboarded to Network as Code, it is provided with client credentials. Developers can obtain their organization credentials via the Dashboard.

NOTE: Remember to subscribe to the Number Verification API first, after which you will be able to click Test Endpoint and use it. Here's a step-by-step on how to subscribe to APIs.

Choose an authorization flowheader link

Both flows use the same OAuth consent steps on the end-user device. They differ in how your backend passes authorization to the verify API.

Fast authorization flowStandard authorization flow
Best forSimpler integration; fewer server-side stepsFull OAuth2 control on your backend
Authorization URLfast_flow_csp_auth_endpoint from well-known metadataauthorization_endpoint from well-known metadata
Token exchangeSkipped — NaC exchanges the authorization code for youYou call the token_endpoint to obtain an access token
Verify API authcode and state query parametersAuthorization: Bearer access token

The sections below walk through each flow. Both flows start with the same client credentials step.

Getting client credentialsheader link

Getting your client credentials is the first step for either flow:

from network_as_code import NetworkAsCodeApi

client = NetworkAsCodeApi(
    rapidapi_host="network-as-code.nokia.rapidapi.com",
    api_key="YOUR_API_KEY",
)

response = client.oauth.get_client_credentials()

print(response)

The output should be your client ID and secret:

{
    "client_id": "your-client-id",
    "client_secret": "your-client-secret"
}

Redirect callback handlerheader link

After the end user completes consent on their device, the operator redirects to your redirect_uri with an authorization code and the state you supplied. Your backend must handle that callback on the end-user device path (the redirect hits your server, but the user must have opened the authorization URL on the mobile device).

A successful callback looks like:

GET https://example.com/redirect?state=foobar&code=1234

Parse code and state from the query string. Validate that state matches the value you stored when building the authorization link (see State and nonce). The redirect does not return nonce.

A minimal handler:

from fastapi import FastAPI

app = FastAPI()

@app.get("/redirect")
async def get_authorization_code(code: str, state: str):
    # Validate state against the value bound to this user session before continuing.
    print(f"This is your state variable: {state}.")
    return f"This is your authorization code: {code}."

State and nonceheader link

state and nonce are separate OpenID Connect parameters. Do not validate them at the same step.

state (redirect callback)header link

Generate a cryptographically random state (for example UUIDv4) when you build the authorization URL, store it server-side bound to the user session, and include it in the link. When the user returns to your redirect_uri, compare the state query parameter to the stored value. Reject the request if they differ. That mitigates cross-site request forgery (CSRF): an attacker who tricks the browser into completing authorization should not be able to pair the resulting code with your session.

nonce (authorization request and ID token)header link

Generate a separate random nonce, store it server-side with the same session, and send it on the authorization request. It is not returned on the redirect; the browser only receives code and state.

Per OpenID Connect, the nonce you sent must appear as a claim in the id_token returned from the token endpoint. That lets your backend confirm the ID token belongs to the authorization request you started, which mitigates mix-up of authorization codes or ID token replay. Network as Code also checks the operator ID token nonce when exchanging codes on your behalf.

FlowWhat your backend should do with nonce
StandardAfter POST to the token endpoint, decode id_token (JWT) and verify its nonce claim equals the value you stored. Then use access_token for verify.
FastSend nonce on the authorization URL. You do not receive an id_token on your backend; NaC performs the token exchange when you call verify with code and state.

Note: nonce is mandatory in the authorization request. Number verification authorization requests should also set prompt=none, because the mobile network connection authenticates the subscription.

Fast authorization flowheader link

The fast flow uses an abridged OAuth2 path: you skip the single-use access token exchange and pass the authorization code and state directly to the verify API. Network as Code exchanges the code for an access token behind the scenes.

Getting the fast authorization endpointheader link

The well-known metadata endpoint returns the fast authorization URL together with the standard OAuth endpoints:

from network_as_code import NetworkAsCodeApi

client = NetworkAsCodeApi(
    rapidapi_host="network-as-code.nokia.rapidapi.com",
    api_key="YOUR_API_KEY"
)

response = client.well_known_metadata.get_oauth_authorization_server()

print(response)

Example response:

{
  "authorization_endpoint": "https://some-auth-server.net/oauth2/v1/authorize",
  "token_endpoint": "https://some-auth-server.net/oauth2/v1/token",
  "fast_flow_csp_auth_endpoint": "https://some-auth-server.net/oauth2/v1/retrieve_csp_auth_url"
}

Use fast_flow_csp_auth_endpoint as the base URL when building the authorization link in the next step.

Getting the authorization codeheader link

Construct an authorization URL from fast_flow_csp_auth_endpoint with the parameters below. The end user's device must open this URL (not your backend) so the operator can bind consent to that device.

ParametersTypeDescriptionMandatory or Optional
authorization_endpointstringUse fast_flow_csp_auth_endpoint from the previous step, e.g. https://some-auth-server.net/oauth2/v1/retrieve_csp_auth_url.Mandatory
scopestringConsent purpose and scope: dpv:FraudPreventionAndDetection number-verification:verify.Mandatory
response_typestringMust be code.Mandatory
client_idstringClient ID from Getting client credentials.Mandatory
redirect_uristringYour backend GET callback, e.g. https://example.com/redirect.Mandatory
login_hintstringEnd-user phone number in E.164 form, e.g. "+99999991000", to route the request to the right network.Mandatory
promptstringMust be none for number verification (network-based authentication).Mandatory
statestringRandom value stored server-side; returned on redirect. Validate at callback. See State and nonce.Mandatory
noncestringRandom value stored server-side; sent on authorize, echoed in id_token (standard flow). See State and nonce.Mandatory

URL-encode the query string:

{authorization_endpoint}?scope={scope}&state={state}&response_type=code&prompt=none&client_id={client_id}&redirect_uri={redirect_uri}&login_hint={login_hint}&nonce={nonce}

Present the link to the user (button, redirect, or in-app web view). On success, the browser calls your redirect callback handler, typically with both code and state in the query string.

Continue to Verifying a number using the fast-flow examples (pass code and state to the verify API).

Standard authorization flowheader link

The standard flow follows typical OAuth2: after the redirect callback you exchange the authorization code for a single-use access token, then call verify with that token.

Getting the authorization and token endpointsheader link

from network_as_code import NetworkAsCodeApi

client = NetworkAsCodeApi(
    rapidapi_host="network-as-code.nokia.rapidapi.com",
    api_key="YOUR_API_KEY"
)

response = client.well_known_metadata.get_oauth_authorization_server()

print(response)

For the standard flow you use authorization_endpoint and token_endpoint:

{
    "authorization_endpoint": "https://some-auth-server.example.com/oauth2/v1/authorize",
    "token_endpoint": "https://some-auth-server.example.com/oauth2/v1/token"
}

Getting the authorization codeheader link

Build an authorization link from authorization_endpoint using the same parameter table as the fast flow, but set authorization_endpoint to the standard authorize URL (not fast_flow_csp_auth_endpoint).

ParametersTypeDescriptionMandatory or Optional
authorization_endpointstringauthorization_endpoint from well-known metadata, e.g. https://some-auth-server.example.com/oauth2/v1/authorize.Mandatory
scopestringdpv:FraudPreventionAndDetection number-verification:verify.Mandatory
response_typestringMust be code.Mandatory
client_idstringClient ID from Getting client credentials.Mandatory
redirect_uristringYour backend GET callback, e.g. https://example.com/redirect.Mandatory
login_hintstringEnd-user phone number, e.g. "+99999991000".Mandatory
promptstringMust be none for number verification.Mandatory
statestringValidated on redirect. See State and nonce.Mandatory
noncestringValidated via id_token after token exchange (standard flow). See State and nonce.Mandatory

The user opens the URL on their device. On success, your redirect callback handler receives the NaC authorization code, for example:

https://example.com/redirect?code=your-code-will-appear-here&state=your-state

Obtaining a single-use access tokenheader link

After you have the authorization code, exchange it for an access token. Call the token endpoint once per number verification for that end-user device.

curl -X POST https://some-auth-server.example.com/oauth2/v1/token \
     -d client_id=previously-obtained-client-id \
     -d client_secret=previously-obtained-client-secret \
     -d grant_type=authorization_code \
     -d code=previously-obtained-nac-authorization-code
ParametersTypeDescriptionMandatory or Optional
client_idstringFrom Getting client credentials.Mandatory
client_secretstringFrom Getting client credentials.Mandatory
grant_typestringMust be authorization_code.Mandatory
codestringAuthorization code from the redirect callback.Mandatory

Successful response:

{
    "access_token": "this-is-your-token",
    "token_type": "Bearer",
    "expires_in": 1723475356,
    "id_token": "eyJ..."
}

Decode id_token and verify that its nonce claim matches the nonce you stored when building the authorization URL. Only then trust the token pair and continue to Verifying a number using the standard-flow examples (Bearer access token).

Verifying a numberheader link

Call the verify API with the phone number to check. Use login_hint during authorization and the same number (or its hash) in the verify request.

Standard flow (access token)header link

Pass the access token via the Authorization header. With the SDK, set authorization to the full header value (for example Bearer plus the token from the token endpoint).

from network_as_code import NetworkAsCodeApi

client = NetworkAsCodeApi(
    rapidapi_host="network-as-code.nokia.rapidapi.com",
    api_key="YOUR_API_KEY"
)

token = "Bearer YOUR_ACCESS_TOKEN"

response = client.number_verification.verify(
    authorization=token,
    phone_number="+99999991000",
)

print(response.device_phone_number_verified)

Fast flow (authorization code and state)header link

Pass the code and state from your redirect callback as query parameters. They must match the values used when building the authorization link.

from network_as_code import NetworkAsCodeApi

client = NetworkAsCodeApi(
    rapidapi_host="network-as-code.nokia.rapidapi.com",
    api_key="YOUR_API_KEY"
)

state = "YOUR_STATE"
code = "YOUR_AUTHORIZATION_CODE"

response = client.number_verification.verify(
    state=state,
    code=code,
    phone_number="+99999991000",
)

print(response.device_phone_number_verified)

Verify responseheader link

A successful verify returns a JSON object with devicePhoneNumberVerified (not a bare boolean). See Number Verification API HTTP responses for full response and error shapes.

FieldTypeDescription
devicePhoneNumberVerifiedbooleantrue if the supplied phone number matches the SIM on the authenticated device; false otherwise.

In the SDK, read device_phone_number_verified (Python) or devicePhoneNumberVerified (TypeScript).

Return the device phone numberheader link

To retrieve the authenticated device's MSISDN instead of comparing a number, use the phone number share operation after the same OAuth flows. The response field is devicePhoneNumber; see the Number share tabs on the HTTP responses page.

response = client.number_verification_v100.phone_number_share(
    authorization="Bearer YOUR_ACCESS_TOKEN",
)
print(response.device_phone_number)

For the fast flow, pass code and state instead of authorization, in the same way as verify.

Simulated Number Verification scenariosheader link

The Network as Code simulators have been configured to provide specific number verification results for specific simulated devices based on their device identifier. This will be helpful in testing your code against the different responses, including possible errors, by simply substituting the device identifier in your code.

The device identifiers and their responses can be found in the following table:

Device identifier typedevice identifierHTTP status codeHTTP status code descriptionResponse description
Phone Number+99999991000200SuccessNumber verifies correctly
Phone Number+99999991001200SuccessNumber is not verified
Phone Number+99999990400400Bad Request
Phone Number+99999990404404Not found
Phone Number+99999990422422Unprocessable Content
Phone Number+99999990500500Internal Server Error
Phone Number+99999990502502Bad Gateway
Phone Number+99999990503503Service Unavailable
Phone Number+99999990504504Gateway Timeout

Last updated September 18, 2026