Device roaming

The Device Status SDK allows checking the device roaming status and subscribing to its related events. A device can be in national (different networks in the same country) or international roaming (different networks in another country). Knowing whether the device is connected to a network other than its home one is important for regulatory, security and content delivery reasons.

For example, in some cases for certain transactions to take place, a device needs to be within an area where it is legally allowed to operate or make transactions. Another case would be to ensure a customer's device is exactly where it claims to be to avoid fraud. Other important examples include content or service delivery to avoid extra costs or unexpected roaming charges for accessing them.

Subscribe to Roaming eventsheader link

The SDK below allows you to subscribe client devices to Device Status roaming events.

import network_as_code as nac
from network_as_code.models.device_status import EventType

# We begin by creating a Network as Code client
client = nac.NetworkAsCodeClient(
    token="<your-application-key-here>"
)

# Then, we create an object for the mobile device we want to use
my_device = client.devices.get(
    # The phone number does not accept spaces or parentheses
    phone_number="+99999991000"
)

# Then we subscribe your device to roaming events.
my_subscription = client.connectivity.subscribe(
    event_type=EventType.ROAMING_STATUS,
    device=my_device,
    # You can tell when the subscription is supposed to expire
    subscription_expire_time="2024-04-10T14:13:29Z",
    # Use HTTPS to send notifications
    notification_url="https://example.com/notify",
    notification_auth_token="replace-with-your-auth-token"
)

# Use this to show the roaming subscription status
print(my_subscription)

Roaming subscription parametersheader link

ParametersTypeDescriptionMandatory or Optional
event_typeobject/stringThe status type you want to check. For example EventType.ROAMING_STATUS or org.camaraproject.device-status.v0.roaming-status.Mandatory
deviceobjectThe device object previously created for the mobile device we want to use.Mandatory
notification_urlstringThe recipient's HTTP endpoint, which is a web server configured to receive POST requests.Mandatory
notification_auth_tokenstringA password used to identify the sender of the notification.Optional
subscription_expire_timeobject/stringWhen the subscription should expire. It can be either a date-time object or ISO 8601 formatted date string, for example "2025-08-28T12:40:20.398Z".Optional

Roaming event typesheader link

Event typeTypeDescription
roaming-statusobject/stringEventType.ROAMING_STATUS or org.camaraproject.device-status.v0.roaming-status - Used for receiving updates about when the device switches from roaming ON to roaming OFF and conversely.
roaming-onobject/stringEventType.ROAMING_ON or org.camaraproject.device-status.v0.roaming-on - Used to receive updates when the device switches from roaming OFF to roaming ON.
roaming-offobject/stringEventType.ROAMING_OFF or org.camaraproject.device-status.v0.roaming-off - Used to receive updates when the device switches from roaming ON to roaming OFF.
roaming-change-countryobject/stringEventType.ROAMING_CHANGE_COUNTRY or org.camaraproject.device-status.v0.roaming-change-country - Notification is sent when the device in roaming changes country code.

Simulated device roaming scenariosheader link

The Network as Code simulators have been configured to provide specific device roaming 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 descriptionReponse description
Phone Number+99999991000200SuccessDevice is roaming
Phone Number+99999991001200SuccessDevice is not roaming
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

Getting device roaming statusheader link

You can check the device roaming status like so:

  print(my_device.get_roaming())

Getting device roaming status responsesheader link

Getting a devices roaming status will respond with a boolean value indicating whether the device is roaming or not. If the device is roaming, the response will also contain the county code and country name the device is roaming in.

ResponseTypeDescription
roaming=TruestringIndicates, that the device is roaming in specified country. For example roaming=True country_code=416 country_name=['JO'].
roaming=FalsestringIndicates, that the device is not roaming.

Getting roaming notificationsheader link

The code snippet below will set up an HTTP server with a POST endpoint. This will allow receiving device roaming status updates. Learn more about the notification URL/auth token and how to create an HTTP server with a POST endpoint for roaming notifications.

Roaming notification handlerheader link

# status_handler.py

# run with: uvicorn status_handler:app

from fastapi import FastAPI, Header
from pydantic import BaseModel

from typing_extensions import Annotated
from typing import List, Union

app = FastAPI()

class Device(BaseModel):
    phoneNumber: str | None
    networkAccessIdentifier: str | None
    ipv4Address: str | None
    ipv6Address: str | None

class RoamingEventDetail(BaseModel):
    device: Device
    subscriptionId: str
    roaming: bool | None
    countryCode: int | None
    countryName: List[str] | None
    terminationReason: str

class Event(BaseModel):
    eventTime: str
    eventDetail: RoamingEventDetail

class Data(BaseModel):
    device: Device
    subscriptionId: str
    roaming: RoamingEventDetail

class RoamingStatusNotification(BaseModel):
    id: str
    source: str
    type: str
    specversion: str
    datacontenttype: str
    time: str
    eventSubscriptionId: str
    event: Event
    data: Data

@app.post("/notifications")
def receive_notification(
    notification: RoamingStatusNotification,
    authorization: Annotated[Union[str, None], Header]
):
    if authorization == "Bearer my-token":
        # We can now react to the notifications
        # based on the Notification object
        print(notification)

Roaming notification detailsheader link

This is the notification JSON schema for roaming related notifications.

Remember that the string values represented below are just examples that can be used. So, they should contain your real device-status values.

{
    "id": "2628b42e-8789-4fcd-942a-841f16f52897",
    "source": "/device-status/v0/subscriptions/90409561-68f5-4360-b12d-9003f4dca8b0",
    "type": "org.camaraproject.device-status.v0.roaming-status",
    "specversion": "1.0",
    "datacontenttype": "application/json",
    "time": "2026-04-17T07:31:01.416474Z",
    "data": {
        "device": {
            "phoneNumber": "+99999991000",
            "networkAccessIdentifier": "[email protected]",
            "ipv4Address": {
                "publicAddress": "233.252.0.2",
                "privateAddress": "192.0.2.25",
                "publicPort": 80
            },
            "ipv6Address": "2001:db8:1234:5678:9abc:def0:fedc:ba98"
        },
        "subscriptionId": "90409561-68f5-4360-b12d-9003f4dca8b0",
        "roaming": true,
        "countryCode": 587
    }
}

Roaming event key wordsheader link

Roaming-event-detail-keyword valuesTypeDescription
roamingboolean/nullTrue, false or null value indicating wheather the device is roaming or not.
countryCodeinteger/nullEither a contry code indicating which country the device is roaming in or a null value. For example "countryCode":222.
countryNamestring/string arrayEither country name(s) indicating which country a device is roaming in or a null value. For example "countryName":["IT"].

Shared-keyword valuesheader link

Check the table below for further information on mandatory, optional values and their types. The values described here are common to all the Device-Status notification JSON schemas:

Keyword valuesTypeDescription
subscriptionIdstringThe event subscription identifier.
sourcestringIdentifies the source where the event happened. It contains the Network as Code API implementation and subscription ID.
typestringThe status type being checked and returned by the API. For example roaming-on.
specversionstringRepresenting the specification version.
datacontenttypestringIndicates the media type for the event payload encoding. It must be "application/json" in the context of CAMARA APIs
timestringThe time the event occurred. Date and time are specified in ISO 8601 format, e.g.: "2023-08-20T21:01:02.345Z".

Device identifiersheader link

Keyword valuesTypeDescriptionMandatory or Optional
dataobjectObject that will contain other objects or strings. Contains multiple device status data, e.g. the subscription id.Mandatory
deviceobjectObject that will contain other objects or strings. Contains multiple device identifiers, e.g. the devices phone number.Mandatory
phoneNumberstringPhone number of the device, with a pattern that matches "^[+]?[0-9]{5,15}$" or a null value.Optional
networkAccessIdentifierstringAn email-like external identifier for a device (or subscriber) into the network, with a pattern that matches "^[+]?[0-9]{5,15}$" or a null value. If both the networkAccessIdentifier and phoneNumber are included, phoneNumber will be dropped and networkAccessIdentifier will be retained.Optional
ipv4AddressobjectContains an object for IPv4 address values or a null value. It refers to the IPv4 address of the device. An IP address is needed for some flow-oriented services, such as QoD.Optional
publicAddressstringEither the device's public IPv4 address, the Network Address Translation (NAT) behind it or a null value. Learn more about NAT..Optional
privateAddressstringThe device's private IPv4 address if it is behind NAT or a null value.Optional
publicPortintegerThe public port used by the device or a null value. A port is necessary, as private address ranges overlap, and public port is used to extend the range for Carrier-grade NAT (CGNAT), a type of large-scale NAT.Optional
ipv6AddressintegerContains the device's IPv6 address or a null value. An IP address is needed for some flow-oriented services, such as QoD.Optional

Last updated November 05, 2025