AI-B100 HTTP API User Guide
Firmware version 1.9.7 | See also: MQTT API User Guide
The AI-B100 also supports MQTT for event-driven integration. If your application connects to an MQTT broker rather than hosting a callback server, see MQTT API User Guide instead.
Introduction
The AI-B100 HTTP API lets your application control the gateway over a standard Ethernet LAN connection. You can join LoRaWAN networks, send uplinks, receive downlinks, change settings, and monitor gateway health — all using plain HTTP requests from any language or platform.
The API follows an asynchronous model: most operational commands (join, send, status) return 202 Accepted immediately, and the actual result arrives later as an HTTP POST from the gateway to a URL on your server. This is because LoRaWAN operations take measurable time — typically several seconds. Your application must expose a small HTTP server to receive these callbacks.
If you prefer a publish-subscribe model without hosting a callback server, consider using the MQTT interface instead.
Before You Start
Before using the HTTP API you need the following in place:
-
Gateway IP address. The gateway must be reachable from your application over Ethernet. If you do not know its IP check your DHCP server's lease table. You can also browse to the gateway's web interface — the IP is shown on the LAN Settings page.
-
A server your application controls. For operational commands (join, send, status, linkcheck) the gateway POSTs results back to a URL you configure. This server must be reachable from the gateway on the same LAN or a routed network. The URL is called the callback.
-
HTTP API enabled. The API is enabled separately from the web interface. Check with:
curl http://<gateway-ip>/get?http_api_enableIf it returns
{"http_api_enable": 0}, enable it:curl -X POST http://<gateway-ip>/set \ -H "Content-Type: application/json" \ -d '{"http_api_enable": 1}'No restart is required for this change.
-
One request at a time. The gateway uses a single network socket. Do not send a second API request until the previous one has completed. This applies to web browser sessions as well — do not leave a settings page open in a browser while your application is making API calls.
Quick Start
The steps below take you from a freshly configured gateway to receiving your first LoRaWAN uplink result. Each step links to the relevant section for details.
Step 1 — Verify the gateway is alive
curl http://<gateway-ip>/info
You should receive a JSON object with firmware version, IP address, and device EUI. If this fails, check network connectivity and the gateway IP. See Query Device Information.
Step 2 — Configure your callback endpoints
Replace 192.168.1.50 and /lorawan/status / /lorawan/rx with your server's IP, port, and paths:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"http_api_enable": 1,
"callback_addr": "192.168.1.50",
"callback_port": 8080,
"callback_status_uri": "/lorawan/status",
"callback_receive_uri": "/lorawan/rx"
}'
Step 3 — Verify LoRaWAN credentials are set
curl http://<gateway-ip>/get?dev_eui
curl http://<gateway-ip>/get?join_eui
If they are wrong or missing, set them now. See LoRaWAN Credentials.
Step 4 — Join the network
curl http://<gateway-ip>/join
The gateway returns 202 Accepted and begins the join procedure. Watch your callback server — you will receive a POST with "status": 7 when the join succeeds.
Step 5 — Send your first uplink
curl -X POST http://<gateway-ip>/send \
-H "Content-Type: application/json" \
-d '{"port": 1, "confirm": 0, "payload": "Hello"}'
Step 6 — Read the result on your callback server
Your callback server receives a POST to /lorawan/status with the send result. If a downlink was returned by the network, your server also receives a POST to /lorawan/rx. See Receive a Downlink.
Core Concepts
The Callback Model
LoRaWAN operations take time: a join attempt waits for a network server response, and an uplink waits for an RX1/RX2 window before any downlink is received. Because of this, the gateway does not hold the HTTP connection open while it works.
Instead:
- You send a request (
GET /join,POST /send, etc.). - The gateway responds immediately with
202 Accepted— this only means the command was queued, not that it succeeded. - When the operation finishes, the gateway POSTs the result to your configured callback URL.
What this means for your application:
- You need an HTTP server that can accept incoming POST requests from the gateway.
- The callback server must be on an IP address the gateway can reach.
- Results are never in the
202 Acceptedresponse — always wait for the callback.
What happens if the callback server is unreachable?
The gateway attempts the callback once. If the connection fails, or the server returns an HTTP error (4xx/5xx), the gateway retries up to a threshold and then enters a 30-second backoff before trying again. The result is not lost — it is queued and will be delivered when connectivity is restored.
Gateway Name and the $name$ Tag
Each gateway has a configurable name (up to 16 characters, letters, digits, hyphens, and underscores). The default is AI-B100.
The name is included as the "name" field in every callback payload — this lets you identify which gateway sent a callback when you have multiple units posting to the same server endpoint.
You can also embed $name$ inside callback path strings. It is replaced with the configured gateway name when each callback is sent:
| Setting stored | Path used at runtime |
|---|---|
/gateway/$name$/status | /gateway/my-gw-01/status |
/lorawan/$name$/rx | /lorawan/my-gw-01/rx |
This lets you route callbacks from multiple gateways to a single server without changing the path configuration on each unit — only the name needs to differ.
Data Rate and Duty Cycle
Data rate controls the balance between range and payload capacity:
data_rate | Spreading factor | Max payload (maxUp) |
|---|---|---|
| 0 | SF12 (longest range) | 51 bytes |
| 1 | SF11 | 51 bytes |
| 2 | SF10 | 51 bytes |
| 3 | SF9 | 115 bytes |
| 4 | SF8 | 222 bytes |
| 5 | SF7 (shortest range) | 222 bytes |
The maxUp field in every status callback tells you the maximum payload size allowed at the current data rate. Always check maxUp before sending — if your payload is larger, the gateway will reject it with status: 3.
Duty cycle limits how often a device can transmit on a given frequency band. When a send is blocked by duty cycle, the callback returns status: 13 and includes next_upload_ms — the number of milliseconds you must wait before the next uplink will be accepted. A value of 0 means there is no active restriction.
Status Codes at a Glance
The status field in every callback payload carries a numeric code. Here is what each one means and what action (if any) to take:
| Code | Event | What to do |
|---|---|---|
| 0 | Ready — gateway joined and idle | No action needed |
| 1 | Gateway started, waiting for join | Send GET /join |
| 2 | Send attempted but payload was empty | Check your /send request body |
| 3 | Payload too long for current data rate | Reduce payload or increase data rate |
| 4 | Join attempt failed | Retry with GET /join, or wait for automatic retry |
| 5 | Gateway started, auto join in progress | Wait for status 7 |
| 6 | Unknown LoRaWAN error | Check LoRaWAN credentials and coverage |
| 7 | Joined successfully | Ready to send |
| 8 | Downlink received (see receive callback) | Read receive callback payload |
| 9 | Uplink sent, no downlink | Normal — unconfirmed send succeeded |
| 10 | Uplink confirmed by network | Normal — confirmed send acknowledged |
| 11 | Uplink not confirmed (no ACK) | Retry if critical; check join_retry and coverage |
| 13 | Duty cycle restriction — uplink blocked | Wait next_upload_ms before retrying |
| 14 | LoRaWAN session lost | Send GET /join to rejoin |
| 15 | Invalid LoRaWAN port number | Use FPort 1–223 |
| 16 | Uplink failed (radio error) | Retry; check hardware |
| 17 | Parameter validation error | Check parameter names and values in your request |
| 18 | Command rejected — not joined yet | Join first, then retry |
| 19 | Setting saved successfully | No action needed |
Configuring the Gateway
Reading Current Settings
GET /get returns all current parameters as a JSON object. You can also request a single parameter by name.
# Get everything
curl http://<gateway-ip>/get
# Get one parameter
curl http://<gateway-ip>/get?callback_addr
Response (single parameter example):
{"callback_addr": "192.168.1.50"}
Changing Settings
POST /set updates one or more parameters in a single request. All parameters are validated before any are saved — if one fails, none are written.
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{"adr_enable": 1, "data_rate": 3}'
Successful response:
{"status": 19, "restart_required": 0}
If any parameter changes require a restart, restart_required is 1. Apply changes that need a restart by calling GET /restart after the /set call.
If a parameter is invalid:
{"status": 17, "failed": "data_rate"}
The failed field names the first parameter that did not validate. Fix it and try again.
Network Settings
By default the gateway uses DHCP. To assign a static IP:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"dhcp_enable": 0,
"ip_addr": "192.168.1.131",
"gateway_addr": "192.168.1.1",
"dns_addr": "192.168.1.1",
"subnet_mask": "255.255.255.0"
}'
This requires a restart. After restart, connect to the new IP. The old IP will no longer respond.
To return to DHCP:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{"dhcp_enable": 1}'
Followed by GET /restart.
Callback Settings
Callbacks are the mechanism the gateway uses to deliver results to your application. Five settings control where callbacks are sent:
| Parameter | Description |
|---|---|
callback_addr | IPv4 address of your callback server |
callback_port | TCP port your callback server listens on |
callback_status_uri | Path for status callbacks (join results, send results) |
callback_receive_uri | Path for receive callbacks (downlinks, linkcheck results) |
callback_gps_uri | Path for GPS position callbacks (optional) |
Example — configure callbacks and enable the API in one request:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"http_api_enable": 1,
"callback_addr": "192.168.1.50",
"callback_port": 8080,
"callback_status_uri": "/lorawan/status",
"callback_receive_uri": "/lorawan/rx"
}'
No restart is needed for callback settings.
Using the $name$ tag in callback paths:
If you run multiple gateways posting to the same server, embed $name$ in the path. The gateway replaces it with the configured gateway name on each callback:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"callback_status_uri": "/lorawan/$name$/status",
"callback_receive_uri": "/lorawan/$name$/rx"
}'
With name set to gw-01, callbacks arrive at /lorawan/gw-01/status and /lorawan/gw-01/rx.
LoRaWAN Credentials
LoRaWAN OTAA credentials must match what is registered on the network server. They require a restart after being changed.
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"dev_eui": "9E139EFFFE98DC98",
"join_eui": "0000000000000000",
"app_key": "00112233445566778899AABBCCDDEEFF"
}'
Then restart:
curl http://<gateway-ip>/restart
| Parameter | Description | Length |
|---|---|---|
dev_eui | Device EUI — unique identifier for this device | 16 hex chars |
join_eui | Join/Application EUI — identifies the application on the network server | 16 hex chars |
app_key | Application key — used to derive session keys during join | 32 hex chars |
nwk_key | Network key — required for LoRaWAN 1.1 only | 32 hex chars |
Gateway Name
Set a name to identify this gateway in callback payloads and callback paths. The name appears as the "name" field in every callback.
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{"name": "warehouse-01"}'
Allowed characters: letters (A–Z, a–z), digits (0–9), hyphen (-), underscore (_). Maximum 16 characters. No restart is required.
Restarting the Gateway
Required after any parameter marked with an X in the restart column of the All Parameters table.
curl http://<gateway-ip>/restart
The gateway responds with 202 Accepted and restarts within two seconds. Poll GET /info to confirm it is back:
# Wait a few seconds, then:
curl http://<gateway-ip>/info
Operations
Check Gateway Status
Requests the current gateway status. The result arrives on your callback_status_uri.
curl http://<gateway-ip>/status
Returns 202 Accepted. Your callback server receives a status payload shortly after. A typical joined-and-ready response:
{
"name": "warehouse-01",
"status": 0,
"dev_addr": "26011BDA",
"confirmed": 1,
"fcntUp": 592,
"data_rate": 3,
"maxUp": 115,
"tUnix": 1775036333,
"next_upload_ms": 0
}
status: 0 with a non-zero dev_addr confirms the gateway is joined and ready to send.
status callback configured. Returns 412 Precondition Failed if not.Join the LoRaWAN Network
Initiates an OTAA join. If already joined, the gateway rejoins.
Simple join:
curl http://<gateway-ip>/join
Join with parameters:
curl "http://<gateway-ip>/join?data_rate_join=0&adr_enable=1&data_rate=3&join_retry=5"
Or via POST:
curl -X POST http://<gateway-ip>/join \
-H "Content-Type: application/json" \
-d '{
"data_rate_join": 0,
"adr_enable": 1,
"data_rate": 3,
"join_retry": 5
}'
Join parameters:
| Parameter | Description | Default |
|---|---|---|
data_rate_join | Data rate for join requests (0=SF12, 5=SF7) | 0 |
adr_enable | Enable Adaptive Data Rate | 1 |
data_rate | Data rate to use after joining | 3 |
join_retry | Max join attempts per round before stopping | 5 |
Setting any of these via /join also saves them for future sessions.
Retry behavior:
The gateway retries automatically up to join_retry times before stopping. Each failed attempt delivers status: 4 to your callback. Backoff between attempts:
| Attempt number | Delay before next attempt |
|---|---|
| 1–3 | 15 seconds |
| 4–10 | 60 seconds |
| 11 and above | 120 seconds |
Once the retry limit is reached, the gateway waits for a manual GET /join before starting a new round.
status callback configured. Returns 412 Precondition Failed if not.Send an Uplink
Queues a LoRaWAN uplink. The send result arrives on your callback_status_uri. If the network sends a downlink, it arrives on your callback_receive_uri.
curl -X POST http://<gateway-ip>/send \
-H "Content-Type: application/json" \
-d '{"port": 1, "confirm": 0, "payload": "Hello"}'
Fields:
| Field | Required | Description |
|---|---|---|
port | Yes | LoRaWAN FPort, 1–223 |
payload | Yes | Data to send — see payload formats below |
confirm | No | 1 = confirmed uplink (requests ACK), 0 = unconfirmed. Default: 0 |
encoding | No | Set to "base64" to send a Base64-encoded payload string. The gateway decodes it to raw bytes before transmission. Omit for formats A, B, and C. |
length | No | Override byte count. Must match actual payload length if provided. When encoding is "base64", this is the decoded byte count, not the Base64 string length. |
Payload formats:
Format A — ASCII string. Use when your data is text. The string is sent byte-for-byte.
{"port": 1, "confirm": 0, "payload": "Hello"}
Format B — Byte array. Use when your data is binary. Each element must be 0–255.
{"port": 1, "confirm": 1, "payload": [0x01, 0x02, 0xFF]}
Format C — Node.js Buffer object. JSON.stringify() on a Node.js Buffer produces this envelope. The gateway unwraps it automatically — use this format as-is from Node-RED or JavaScript.
{"port": 1, "confirm": 0, "payload": {"type": "Buffer", "data": [0, 1, 2, 3]}}
Format D — Base64 string. Use when your data originates as Base64. Set "encoding": "base64" and put the Base64-encoded bytes in payload. The gateway decodes them to raw bytes before transmitting over LoRaWAN.
{"port": 1, "confirm": 0, "encoding": "base64", "payload": "AQID/w=="}
"AQID/w==" decodes to the four bytes [0x01, 0x02, 0x03, 0xFF]. If you supply length, it must equal the decoded byte count (here: 4), not the Base64 string length.
Send result status codes:
status | Meaning |
|---|---|
| 9 | Uplink sent, no downlink received |
| 10 | Uplink confirmed — ACK received from network |
| 11 | Uplink not confirmed — no ACK (try again if critical) |
| 13 | Blocked by duty cycle — check next_upload_ms and retry later |
| 16 | Uplink failed — radio error |
status and receive callbacks configured. Returns 412 Precondition Failed if not. Returns 409 Conflict if not joined.Checking maxUp before sending: The maxUp field in the last status callback tells you how many bytes you can send at the current data rate. Sending more returns status: 3 (payload too long) and the transmission is not attempted.
Receive a Downlink
Downlinks arrive unsolicited on your callback_receive_uri whenever the LoRaWAN network sends a message back to the gateway, or in response to a linkcheck.
A typical downlink with payload:
POST /lorawan/rx HTTP/1.1
Host: 192.168.1.50:8080
Content-Type: application/json
{
"name": "warehouse-01",
"confirmed": 1,
"fcntDown": 38,
"rssi": -70,
"snr": 10.0,
"tUnix": 1775036333,
"next_upload_ms": 0,
"port": 5,
"length": 4,
"payload": [0x01, 0x02, 0x03, 0x04]
}
If the payload is printable ASCII, payload is returned as a string. If any byte is non-printable, it is returned as an array of integers.
A linkcheck response (no payload):
POST /lorawan/rx HTTP/1.1
Host: 192.168.1.50:8080
Content-Type: application/json
{
"name": "warehouse-01",
"confirmed": 1,
"fcntDown": 5,
"rssi": -70,
"snr": 10.0,
"tUnix": 1775036333,
"margin": 22,
"gwCount": 1,
"next_upload_ms": 0,
"length": 0
}
margin and gwCount are only present in linkcheck responses.
Key fields:
| Field | Description |
|---|---|
confirmed | 1 if the previous uplink was acknowledged by the network |
fcntDown | Downlink frame counter — use to detect missed messages |
rssi | Signal strength in dBm |
snr | Signal-to-noise ratio in dB |
tUnix | UTC Unix timestamp (synchronized via network time during linkcheck, 0 if not yet synchronized) |
length | Byte count of payload (0 = no downlink data, linkcheck only) |
margin | Link margin from linkcheck (larger is better) |
gwCount | Number of network gateways that heard the uplink |
Run a Link Check
A link check sends a LinkCheckReq to the LoRaWAN network and receives back signal quality information — useful for verifying coverage without sending application data.
curl http://<gateway-ip>/linkcheck
The result arrives on your callback_receive_uri with margin and gwCount fields and length: 0.
receive callback configured. Returns 409 Conflict if not joined.Query Device Information
GET /info returns a snapshot of gateway state and is always available regardless of whether the HTTP API is enabled.
curl http://<gateway-ip>/info
Example response:
{
"HW": "AI-B100",
"HW_ver": "1.3",
"FW_ver": "1.9.7",
"name": "warehouse-01",
"power": "poe",
"dhcp_enable": 0,
"ip_addr": "192.168.1.131",
"dev_eui": "9E139EFFFE98DC98",
"dev_addr": "26011BDA",
"restart_counter": 12,
"mqtt_enable": 0,
"http_api_enable": 1,
"callback": "active",
"tUnix": 1775036333,
"TempC": 29.75
}
| Field | Description |
|---|---|
dev_addr | "0" = not joined; 8 hex chars = joined device address |
callback | "active" = server reachable; "fail" = server unreachable; "disabled" = no callback paths set |
tUnix | 0 = network time not yet synchronized |
TempC | Board temperature — only present on hardware revision 1.3 |
restart_counter | Counts every hardware restart; useful for detecting unexpected reboots |
Lifecycle Scenarios
These scenarios cover the typical application lifecycle. Each assumes callbacks are already configured. See Callback Settings if they are not.
Scenario 1: Manual Join
The gateway waits for an explicit join command. Use this when your application controls when joining happens.
Prerequisite: autojoin_enable = 0
Step 1 — Gateway powers up.
Your callback server receives:
{
"name": "warehouse-01",
"status": 1,
"dev_addr": "0",
"confirmed": 0,
"fcntUp": 0,
"data_rate": 3,
"maxUp": 115,
"tUnix": 0,
"next_upload_ms": 0
}
status: 1 means the gateway is ready but waiting for your join command. dev_addr: "0" confirms it is not yet joined.
Step 2 — Your application triggers a join:
curl http://<gateway-ip>/join
Returns 202 Accepted. The gateway retries up to join_retry times automatically. No action is needed during retries.
Step 3 — Join succeeds.
Your callback server receives:
{
"name": "warehouse-01",
"status": 7,
"dev_addr": "26011BDA",
"confirmed": 0,
"fcntUp": 0,
"data_rate": 3,
"maxUp": 115,
"tUnix": 0,
"next_upload_ms": 0
}
status: 7 with a non-zero dev_addr confirms the join succeeded. Continue to Scenario 4.
Scenario 2: Auto Join
The gateway joins automatically after power-up. No join command is needed.
Prerequisite: autojoin_enable = 1
Step 1 — Gateway powers up and begins joining automatically.
Your callback server receives status: 5. No action required.
Step 2 — Join succeeds.
Your callback server receives status: 7. Continue to Scenario 4.
If all join_retry attempts fail, the gateway stops and waits for a manual GET /join. See Scenario 3.
Scenario 3: Join Retry Limit Reached
Applies when all attempts in a join round fail — either on auto join or after a manual GET /join.
Each failed attempt delivers:
{
"name": "warehouse-01",
"status": 4,
"dev_addr": "0",
...
}
After join_retry consecutive failures the gateway stops. When you are ready to retry:
# Simple retry with current settings
curl http://<gateway-ip>/join
# Retry with updated retry count
curl "http://<gateway-ip>/join?join_retry=10"
Scenario 4: Normal Operation
Use this flow once the gateway is joined.
Sending an uplink:
curl -X POST http://<gateway-ip>/send \
-H "Content-Type: application/json" \
-d '{"port": 1, "confirm": 1, "payload": "Hello"}'
Your callback server receives the result:
status | Meaning |
|---|---|
| 9 | Sent, no downlink |
| 10 | Sent and confirmed |
| 13 | Blocked by duty cycle — wait next_upload_ms ms |
If the LoRaWAN session is lost:
Your callback server receives status: 14. Send GET /join to start a new join round, or wait for the gateway to rejoin automatically if autojoin_enable = 1.
GPS
GPS functionality requires a GPS module to be connected. If no module is present the /gps endpoint still returns a response but with all fields zeroed and gps_status: 0.
Query Current Position
curl http://<gateway-ip>/gps
Example response with a 3D fix:
{
"name": "warehouse-01",
"ns": "N",
"lat": 55.6761,
"ew": "E",
"lon": 12.5683,
"alt": 12.5,
"nosv": 8,
"pdop": 1.8,
"hdop": 1.2,
"vdop": 1.4,
"utc": "103045.00",
"date": "090526",
"sog": 0.0,
"cog": 0.0,
"gps_status": 3
}
gps_status values:
| Value | Meaning |
|---|---|
| 0 | No GPS module detected |
| 1 | Module present but no fix yet |
| 2 | 2D fix (latitude/longitude valid, altitude not reliable) |
| 3 | 3D fix (latitude, longitude, and altitude all valid) |
Coordinate format:
lat and lon are decimal degrees. ns and ew give the hemisphere. A position of lat: 55.6761, ns: "N", lon: 12.5683, ew: "E" is 55.6761°N, 12.5683°E.
utc is a string in HHMMSS.SS format — 103045.00 means 10:30:45.00 UTC.
date is DDMMYY — 090526 means 9 May 2026.
Periodic GPS Callbacks
The gateway can POST GPS position to your server at a regular interval without being polled.
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"callback_gps_uri": "/lorawan/gps",
"gps_update_interval": 60
}'
gps_update_interval is in seconds. Set to 0 to disable. No restart required.
The GPS callback payload is identical to the /gps response — a POST to your callback_gps_uri with the same JSON fields. The $name$ tag is supported in callback_gps_uri.
Callback Authentication
If your callback server requires authentication, the gateway supports HTTP Digest Authentication (RFC 7616, MD5). This ensures the password is never sent in plain text.
How it works:
- The gateway sends the callback POST without any
Authorizationheader. - If your server responds
401 Unauthorizedwith aWWW-Authenticate: Digest ...challenge, and credentials are configured on the gateway, the gateway re-sends the same POST with a computedAuthorization: Digest ...header. - If the credentials are accepted, the callback is processed normally.
Configure credentials:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"callback_digest_user": "admin",
"callback_digest_password": "s3cr3t"
}'
To disable authentication:
curl -X POST http://<gateway-ip>/set \
-H "Content-Type: application/json" \
-d '{
"callback_digest_user": "",
"callback_digest_password": ""
}'
What your server must send on the challenge:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="AI-B100", nonce="abc123xyz", algorithm=MD5, qop="auth"
Content-Length: 0
The gateway supports algorithm=MD5 and qop=auth. A challenge without qop is also accepted. Other algorithms (SHA-256 etc.) are not supported.
What the gateway sends on the authenticated retry:
POST /lorawan/gw-01/status HTTP/1.1
Authorization: Digest username="admin", realm="AI-B100", nonce="abc123xyz",
uri="/lorawan/gw-01/status", algorithm=MD5, qop=auth,
nc=00000001, cnonce="a4f3c1e2",
response="7b3f1c9e2a4d8f6e1b2c3d4e5f6a7b8c"
Reference
Endpoint Quick Reference
| Endpoint | Method | API must be enabled | Returns callback |
|---|---|---|---|
/info | GET | No | No — response is immediate |
/get | GET | No | No — response is immediate |
/set | POST | No | No — response is immediate |
/restart | GET or POST | No | No |
/gps | GET | No | No — response is immediate |
/status | GET | Yes | Status callback |
/join | GET or POST | Yes | Status callback |
/linkcheck | GET | Yes | Receive callback |
/send | POST | Yes | Status callback + receive callback (if downlink) |
All Parameters
LoRaWAN parameters — all require a restart unless noted
| Parameter | Type | Range | Restart |
|---|---|---|---|
lora_version | integer | 0 = LoRaWAN 1.04 A, 1 = 1.10 A, 2 = 1.04 C, 3 = 1.10 C | X |
join_eui | hex string | 16 hex chars | X |
dev_eui | hex string | 16 hex chars | X |
app_key | hex string | 32 hex chars | X |
nwk_key | hex string | 32 hex chars (LoRaWAN 1.1 only) | X |
autojoin_enable | integer | 0 or 1 | |
join_retry | integer | 1–99 | |
data_rate_join | integer | 0–5 (0 = SF12, 5 = SF7) | |
data_rate | integer | 0–5 (0 = SF12, 5 = SF7) | |
adr_enable | integer | 0 or 1 | |
duty_check_enable | integer | 0 or 1 | |
lora_link_check_interval | integer | 0–128 (uplinks between link checks, 0 = disabled) | |
lora_hb_interval | integer | 0–65535 seconds (0 = disabled) | |
lora_hb_port | integer | 1–223 |
Network / LAN parameters
| Parameter | Type | Range | Restart |
|---|---|---|---|
dhcp_enable | integer | 0 or 1 | X |
ip_addr | IPv4 | valid IPv4 address | X |
gateway_addr | IPv4 | valid IPv4 address | X |
dns_addr | IPv4 | valid IPv4 address | X |
subnet_mask | IPv4 | valid IPv4 address | X |
HTTP API and callback parameters — no restart required
| Parameter | Type | Range |
|---|---|---|
http_api_enable | integer | 0 or 1 |
callback_addr | IPv4 | valid IPv4 address |
callback_port | integer | 1–65535 |
callback_status_uri | string | max 32 chars, supports $name$ |
callback_receive_uri | string | max 32 chars, supports $name$ |
callback_gps_uri | string | max 32 chars, supports $name$ |
gps_update_interval | integer | 0–3600 seconds (0 = disabled) |
callback_digest_user | string | max 32 chars (empty = disabled) |
callback_digest_password | string | max 48 chars (empty = disabled) |
MQTT parameters — restart required unless noted
| Parameter | Type | Range | Restart |
|---|---|---|---|
mqtt_enable | integer | 0 or 1 | X |
broker_addr | IPv4 | valid IPv4 address | X |
broker_port | integer | 1–65535 | X |
mqtt_user | string | max 16 chars | X |
mqtt_password | string | max 16 chars | X |
send_topic | string | max 31 chars, supports $name$ | X |
receive_topic | string | max 31 chars, supports $name$ | X |
status_topic | string | max 31 chars, supports $name$ | X |
setup_topic | string | max 31 chars, supports $name$ | X |
gps_topic | string | max 31 chars, supports $name$ | X |
mqtt_hb_interval | integer | 0–65535 seconds (0 = disabled) | |
mqtt_gps_interval | integer | 0–3600 seconds (0 = disabled) |
System parameters
| Parameter | Type | Range | Restart |
|---|---|---|---|
name | string | max 16 chars: A–Z, a–z, 0–9, -, _ | |
watchdog_minutes | integer | 0–65535 minutes (0 = disabled) | |
ota_mode | integer | 0 = off, 1 = once, 2 = auto, 3 = force | |
ota_ssid | string | max 31 chars | |
ota_password | string | max 31 chars |
HTTP Status Codes
Codes returned in the HTTP response to your requests (not to be confused with the LoRaWAN status codes in callback payloads):
| Code | Meaning in AI-B100 context |
|---|---|
| 200 OK | Settings read or written successfully |
| 202 Accepted | Operational command queued — result will arrive via callback |
| 400 Bad Request | Invalid parameter name, value out of range, or malformed JSON |
| 404 Not Found | Unknown endpoint path |
| 405 Method Not Allowed | Wrong HTTP method (e.g. POST to a GET-only endpoint) |
| 409 Conflict | Command rejected because the gateway is not joined |
| 411 Length Required | POST request missing Content-Length header |
| 412 Precondition Failed | Required callback not configured |
| 413 Content Too Large | Payload exceeds current maxUp limit, or JSON body > 1024 bytes |
| 500 Internal Server Error | Join already in progress, or FRAM settings write failed |
| 503 Service Unavailable | HTTP API disabled, or command queue full — retry shortly |
Callback Payload Fields
Status callback fields (delivered to callback_status_uri)
| Field | Type | Description |
|---|---|---|
name | string | Gateway name |
status | integer | Status code — see Status Codes at a Glance |
dev_addr | string | 8-char hex device address, or "0" if not joined |
confirmed | integer | 1 if the last uplink was acknowledged by the network |
fcntUp | integer | Uplink frame counter |
data_rate | integer | Current uplink data rate (0 = SF12, 5 = SF7) |
maxUp | integer | Max payload bytes at current data rate |
tUnix | integer | UTC Unix timestamp in seconds, or 0 if not synchronized |
next_upload_ms | integer | Milliseconds until next uplink allowed (0 = no restriction) |
Receive callback fields (delivered to callback_receive_uri)
| Field | Type | Description |
|---|---|---|
name | string | Gateway name |
confirmed | integer | 1 if the last uplink was acknowledged |
fcntDown | integer | Downlink frame counter |
rssi | float | Signal strength in dBm |
snr | float | Signal-to-noise ratio in dB |
tUnix | integer | UTC Unix timestamp in seconds, or 0 |
margin | integer | Link margin from linkcheck (linkcheck responses only) |
gwCount | integer | Number of gateways that heard the uplink (linkcheck only) |
next_upload_ms | integer | Milliseconds until next uplink allowed |
length | integer | Payload byte count (0 = no downlink data) |
port | integer | LoRaWAN FPort (only present if length > 0) |
payload | string or array | Downlink bytes — string if all printable ASCII, integer array otherwise (only present if length > 0) |
GPS callback fields (delivered to callback_gps_uri)
| Field | Type | Description |
|---|---|---|
name | string | Gateway name |
ns | string | "N" or "S" |
lat | float | Latitude in decimal degrees |
ew | string | "E" or "W" |
lon | float | Longitude in decimal degrees |
alt | float | Altitude in metres above mean sea level |
nosv | integer | Number of satellites in use |
pdop | float | Position dilution of precision |
hdop | float | Horizontal dilution of precision |
vdop | float | Vertical dilution of precision |
utc | string | UTC time HHMMSS.SS |
date | string | Date DDMMYY |
sog | float | Speed over ground in knots |
cog | float | Course over ground in degrees true |
Troubleshooting
412 Precondition Failed on /join, /status, or /send
The required callback is not configured. Check that callback_addr, callback_port, and the relevant URI (callback_status_uri for join/status, callback_receive_uri for send/linkcheck) are all non-empty:
curl http://<gateway-ip>/get?callback_addr
curl http://<gateway-ip>/get?callback_status_uri
curl http://<gateway-ip>/get?callback_receive_uri
Also check that http_api_enable is 1.
409 Conflict on /send or /linkcheck
The gateway is not joined. Call GET /join first and wait for a status: 7 callback before sending.
Gateway returns 202 Accepted but no callback arrives
The gateway cannot reach your callback server. Check:
callback_addris set to your server's IP (not0.0.0.0or the gateway's own IP).- Your server is listening on
callback_port. - No firewall is blocking incoming connections on that port from the gateway's IP.
- Check
GET /info— thecallbackfield shows"active"if the TCP connection succeeds,"fail"if it cannot connect.
curl http://<gateway-ip>/info | grep callback
Join keeps failing (status: 4 repeatedly)
- Credentials — verify
dev_eui,join_eui, andapp_keymatch the network server registration exactly. - Coverage — if the device is indoors or far from a gateway, try
data_rate_join=0(SF12, longest range). - Duty cycle — after several join attempts, the radio enters a mandatory quiet period. Wait a minute and try again.
- Network server — confirm the device is registered and activated on the network server.
Duty cycle blocking sends (status: 13)
The next_upload_ms field in the callback tells you exactly how long to wait. Do not retry before that time elapses.
{"status": 13, "next_upload_ms": 45000, ...}
Wait 45 seconds, then retry. If this happens frequently, consider increasing the uplink interval or switching to a higher data rate (shorter transmit time = less duty cycle consumption).
Payload rejected as too long (status: 3)
Check maxUp in the last status callback — this is the limit at the current data rate. Either shorten the payload or increase data_rate to a higher number (e.g. data_rate: 4 or 5 allows up to 222 bytes). Note that higher data rates have shorter range.
GPS returns all zeros or gps_status: 0
No GPS module is connected, or the module has not yet acquired a fix. gps_status: 1 means the module is present but still searching — this is normal for the first minute or two after power-up, especially indoors. Wait for gps_status: 3 (3D fix) before trusting the coordinates.