While we recommend using the Console or CLI to manage your applications and devices in The Things Stack, we also expose HTTP and gRPC APIs which you can interact directly with. This section contains information about using the HTTP API, and examples.
A complete list of API endpoints is available in the API Reference. There, you can also find detailed information about Authentication and Field Masks.
Warning:
If you are not getting the fields you expect in API responses, see the Field Masks reference.If you are having trouble with the HTTP API, you can always inspect requests in the Console using your browser’s inspector. All of the data displayed in the Console is pulled using HTTP API requests, and this should give you some insight in to how they are formed.
Best Practices
Send a User-Agent
Header
Set the User-Agent
HTTP header containing your integration name and version. That way, a network operator can help finding out potential issues using the logs.
Respect X-Ratelimit-*
Response Headers
The Things Stack sends responses containing information about how many requests your integration has made and how many are remaining, in accordance with the IETF draft spec here.
Mind the X-Warning
Headers
The Things Stack sends responses containing this header to warn about issues that may become errors in the future.
HTTP Query Examples
Get Device Info
Fields may be specified in HTTP requests by appending them as query string parameters. For example, to request the name
, description
, and locations
of devices in an EndDeviceRegistry.Get
request, add these fields to the field_mask
field. To get this data for device dev1
in application app1
:
curl --location \
--header "Authorization: Bearer NNSXS.XXXXXXXXX" \
--header 'Accept: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
'https://thethings.example.com/api/v3/applications/app1/devices/dev1?field_mask=name,description,locations'
The same request in tenant tenant1
on a multi-tenant deployment:
curl --location \
--header "Authorization: Bearer NNSXS.XXXXXXXXX" \
--header 'Accept: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
'https://tenant1.thethings.example.com/api/v3/applications/app1/devices/dev1?field_mask=name,description,locations'
Fields may also be specified as a JSON object in a POST request.
Get Event Stream
To get a stream of events for device dev1
in application app1
:
curl --location \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Accept: text/event-stream' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request POST \
--data-raw '{
"identifiers":[{
"device_ids":{
"device_id":"dev1",
"application_ids":{"application_id":"app1"}
}
}]
}' \
'https://thethings.example.com/api/v3/events'
See here a description of the text/event-stream
MIME type.
Schedule Downlink
To schedule a downlink, you may use the DownlinkQueuePush
or DownlinkQueueReplace
endpoints of the Application Server API. For example, to schedule a downlink queue push to device dev1
in application app1
:
curl --location \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request POST \
--data '{
"downlinks": [{
"frm_payload": "vu8=",
"f_port": 42,
}]
}' \
'https://thethings.example.com/api/v3/as/applications/app1/devices/dev1/down/push'
To schedule a human readable downlink to the same device using a downlink Payload Formatter:
curl --location \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request POST \
--data '{"downlinks":[{
"decoded_payload": {
"bytes": [1, 2, 3]
}
}]
}' \
'https://thethings.example.com/api/v3/as/applications/app1/devices/dev1/down/push'
Please note that downlinks scheduled using the decoded_payload
Payload Formatter field are encrypted in the Application Server, and the content will not be comprehensible in the Network Server’s frm_payload
field when viewing events.
It is also possible to schedule downlinks using HTTP Webhooks, which give you flexibility to choose JSON or gRPC HTTP payloads.
Multi-step Actions
If you want to create a device, perform multi-step actions, or write shell scripts, it’s best to use the CLI.
If you want to do something like registering a device directly via the API, you need to make calls to the Identity Server, Join Server, Network Server and Application Server. See the API Reference for detailed information about which messages go to which endpoints.
To register a device newdev1
in application app1
, first, register the DevEUI
, JoinEUI
and cluster addresses in the Identity Server. This is also where you register a friendly name, description, attributes, location, and more - see all fields in the API Reference:
curl --location \
--header 'Accept: application/json' \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request POST \
--data-raw '{
"end_device": {
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX",
"join_eui": "XXXXXXXXXXXXXXXX"
},
"join_server_address": "thethings.example.com",
"network_server_address": "thethings.example.com",
"application_server_address": "thethings.example.com"
},
"field_mask": {
"paths": [
"join_server_address",
"network_server_address",
"application_server_address",
"ids.dev_eui",
"ids.join_eui"
]
}
}' \
'https://thethings.example.com/api/v3/applications/app1/devices'
Next, you need to register the DevEUI
, JoinEUI
, cluster addresses and keys in the Join Server:
curl --location \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request PUT \
--data-raw '{
"end_device": {
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX",
"join_eui": "XXXXXXXXXXXXXXXX"
},
"network_server_address": "thethings.example.com",
"application_server_address": "thethings.example.com",
"root_keys": {
"app_key": {
"key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
},
"field_mask": {
"paths": [
"network_server_address",
"application_server_address",
"ids.device_id",
"ids.dev_eui",
"ids.join_eui",
"root_keys.app_key.key"
]
}
}' \
'https://thethings.example.com/api/v3/js/applications/app1/devices/newdev1'
Register LoRaWAN® settings in the Network Server:
curl --location \
--header 'Accept: application/json' \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request PUT \
--data-raw '{
"end_device": {
"supports_join": true,
"lorawan_version": "1.0.2",
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX",
"join_eui": "XXXXXXXXXXXXXXXX"
},
"lorawan_phy_version": "1.0.2-b",
"frequency_plan_id": "EU_863_870_TTN"
},
"field_mask": {
"paths": [
"supports_join",
"lorawan_version",
"ids.device_id",
"ids.dev_eui",
"ids.join_eui",
"lorawan_phy_version",
"frequency_plan_id"
]
}
}' \
'https://thethings.example.com/api/v3/ns/applications/app1/devices/newdev1'
Register the DevEUI
and JoinEUI
in the Application Server:
curl --location \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request PUT \
--data-raw '{
"end_device": {
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX",
"join_eui": "XXXXXXXXXXXXXXXX"
}
},
"field_mask": {
"paths": [
"ids.device_id",
"ids.dev_eui",
"ids.join_eui"
]
}
}' \
'https://thethings.example.com/api/v3/as/applications/app1/devices/newdev1'
To register a device newdev1
in application app1
, first, register the DevEUI
and cluster addresses in the Identity Server. This is also where you register a friendly name, description, attributes, location, and more - see all fields in the API Reference:
curl --location \
--header 'Accept: application/json' \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request POST \
--data-raw '{
"end_device": {
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX"
},
"network_server_address": "thethings.example.com",
"application_server_address": "thethings.example.com"
},
"field_mask": {
"paths": [
"network_server_address",
"application_server_address",
"ids.dev_eui"
]
}
}' \
'https://thethings.example.com/api/v3/applications/app1/devices'
Register LoRaWAN settings in the Network Server:
curl --location \
--header 'Accept: application/json' \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request PUT \
--data-raw '{
"end_device": {
"supports_join": false,
"lorawan_version": "1.0.2",
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX"
},
"session": {
"keys": {
"f_nwk_s_int_key": {
"key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
},
"dev_addr": "XXXXXXXX"
},
"mac_settings": {
"resets_f_cnt": true,
"factory_preset_frequencies": [
"868100000",
"868300000",
"868500000",
"867100000",
"867300000",
"867500000",
"867700000",
"867900000"
]
},
"resets_f_cnt": false,
"lorawan_phy_version": "1.0.2-b",
"frequency_plan_id": "EU_863_870_TTN"
},
"field_mask": {
"paths": [
"supports_join",
"lorawan_version",
"ids.device_id",
"ids.dev_eui",
"session.keys.f_nwk_s_int_key.key",
"session.dev_addr",
"mac_settings.resets_f_cnt",
"mac_settings.factory_preset_frequencies",
"lorawan_phy_version",
"frequency_plan_id"
]
}
}' \
'https://thethings.example.com/api/v3/ns/applications/app1/devices/newdev1'
Register the DevEUI
in the Application Server:
curl --location \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--header 'User-Agent: my-integration/my-integration-version' \
--request PUT \
--data-raw '{
"end_device": {
"ids": {
"device_id": "newdev1",
"dev_eui": "XXXXXXXXXXXXXXXX"
},
"session": {
"keys": {
"app_s_key": {
"key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
},
"dev_addr": "XXXXXXXX"
},
"skip_payload_crypto": false
},
"field_mask": {
"paths": [
"ids.device_id",
"ids.dev_eui",
"session.keys.app_s_key.key",
"session.dev_addr",
"skip_payload_crypto"
]
}
}' \
'https://thethings.example.com/api/v3/as/applications/app1/devices/newdev1'
Register a Gateway
To register a gateway gtw1
using the GatewayRegistry.Create
request:
curl --location \
--header 'Accept: application/json' \
--header 'Authorization: Bearer NNSXS.XXXXXXXXX' \
--header 'Content-Type: application/json' \
--request POST \
--data-raw '{
"gateway": {
"ids": {
"gateway_id": "gtw1",
"eui": "XXXXXXXXXXXXXXXX"
},
"name": "my gateway",
"gateway_server_address": "thethings.example.com",
"frequency_plan_id": "EU_863_870_TTN"
}
}' \
'https://thethings.example.com/api/v3/users/user1/gateways'
See here for more info about gateway APIs.
Purge Entities
An admin user can purge entities such as applications, clients, gateways, organizations or users.
For example, to purge the application app1
:
curl --location \
--header "Authorization: Bearer NNSXS.XXXXXXXXX" \
--request DELETE \
'https://thethings.example.com/api/v3/applications/app1/purge'
API Usage Troubleshooting
This section provides help for common issues and frequently asked questions you may have when using the API.
I get a forbidden path(s) in field mask
error.
This error usually occurs when wrong path(s) are specified in the field_mask
object in the API request body. See Fields and Field Masks section and make sure that paths listed under your field_mask
are correct.
When adding a device, I get an invalid end_device: embedded message failed validation
error.
The most common cause for this error is not following the regex pattern in the device_id
field. See End Device APIs section and make sure your device_id
is in line with the defined regex pattern. See also ID and EUI constaints documentation.
Listing gateways via API call works for the eu1
The Things Stack Cloud cluster, but won’t work for the nam1
cluster.
Unlike other server components, the Identity Server component of The Things Stack is hosted only in the eu1
cluster for The Things Stack Cloud and The Things Stack Community Edition. This is the reason why Identity Server API request to any cluster other than eu1
will fail.
I see a 426 Upgrade Required
error when making an API call.
This error indicates that the client HTTP protocol version is old, so the server refuses to perform the request and requires a client update to HTTP 1.1 version or higher.
I get an insufficient_application_rights
error.
The API key you are using doesn’t have sufficient rights. Go to the Console, enter your API key settings on the left hand menu and edit them to provide rights needed to perform the desired action.