Skip to main content

restmesh

Buy me a coffee

A stateless thread-safe REST API for Meshtastic.

Description and features

Send messages on Meshtastic using a standard REST API:

  • thread safe via asyncio
  • safe because a FIFO queue avoid overwhelming the mesh
  • very simple to integrate in other projects: Apprise is already available
  • aims to have 100% unit test coverage
  • doesn't use a database, it merely acts as a gateway
  • no MQTT, WiFI, bluetooth: just plug in the radio via USB, set it as CLIENT_MUTE and enjoy

A typical use case for restmesh is for system error reporting, for example when local Internet is down. You could set up a script to interface with restmesh like this:

#!/usr/bin/env bash

# See:
# https://www.iana.org/domains/root/servers
#
# It is very improbable that 3 root DNS server go simultaneously offline.
#
ANYCAST_1='198.41.0.4'
ANYCAST_2='192.36.148.17'
ANYCAST_3='202.12.27.33'

(nc -zu -w 2 "${ANYCAST_1}" 53 \
 || nc -zu -w 2 "${ANYCAST_2}" 53 \
 || nc -zu -w 2 "${ANYCAST_3}" 53) 2>/dev/null \
&& anycast_ok='true' || anycast_ok='false'

if [ "${anycast_ok}" = 'false' ]; then
    echo 'Internet unreachable, alerting mesh channel'

    # Use Meshtastic channel 1 (a non-primary channel).
    # Channel IDs are the same reported in the mobile app.
    #
    # You need to install apprise first via pip or your distro's package
    # manager.
    apprise -b 'ERROR: Internet unreachable' "json://127.0.0.1:8000/api/v1/integrations/apprise/channels/1/messages"
fi

Quickstart

Installation

pip install restmesh

CLI help

usage: restmesh [-h] [--host HOST] [--port PORT] [--radio-serial-path RADIO_SERIAL_PATH]

restmesh: stateless thread-safe REST API for Meshtastic

options:
  -h, --help            show this help message and exit
  --host HOST           Server host listening address (default: 127.0.0.1)
  --port PORT           Server listening port (default: 8000)
  --radio-serial-path RADIO_SERIAL_PATH
                        Path of the USB serial device radio (default: /dev/ttyUSB0)

Running

Defaults

restmesh --host 127.0.0.1 --port 8000 --radio-serial-path /dev/ttyUSB0

Connect to http://127.0.0.1/docs for the Swagger page to test the endpoints, or use Apprise directly.

Globally

restmesh --host 0.0.0.0 --port 8000 --radio-serial-path /dev/ttyUSB0

[!WARNING] At the moment no kind of authentication is implemented!

Use the Core API

Send to channel 0 (the primary channel):

curl -X 'POST' \
'http://127.0.0.1:8000/api/v1/channels/0/messages' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "text": "This is a message for the mesh on channel 0!",
  "wantAck": false,
  "portNum": 1
}'

Send to node !0a1b2c3d:

curl -X 'POST' \
  'http://127.0.0.1:8000/api/v1/nodes/%210a1b2c3d/messages' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "text": "This is a message for node !0a1b2c3d",
  "wantAck": false,
  "wantResponse": true,
  "portNum": 1
}'

REST API reference

This endpoint documentation is automatically generated from FastAPI OpenAPI's generator and swagger-markdown.


[POST] /api/v1/channels/{channel_index}/messages

Send a message to a channel

Broadcast a text message to a specific mesh channel.

Parameters

Name Located in Description Required Schema
channel_index path The channel index (0 to 7) Yes integer

Request Body

Required Schema
Yes application/json: ChannelBroadcastPayload

Responses

Code Description Schema
202 Successful Response application/json: MeshActionResponse
422 Validation Error application/json: HTTPValidationError
429 Unable to handle more requests because the FIFO queue is full application/json: QueueErrorResponse
503 Meshtastic radio problem. Different errors can be returned. application/json: RadioErrorResponse

[POST] /api/v1/nodes/{node_target}/messages

Send Text To Node

Send a DM text to a node via nodeId string or nodeNum integer.

Parameters

Name Located in Description Required Schema
node_target path Target destination: can be a lowercase hex string NodeId (e.g. !2c3b4f5a) or a numeric NodeNum < 2^32 (e.g. 60). Yes string or integer

Request Body

Required Schema
Yes application/json: NodeDirectPayload

Responses

Code Description Schema
202 Successful Response application/json: MeshActionResponse
422 Validation Error application/json: HTTPValidationError
429 Unable to handle more requests because the FIFO queue is full application/json: QueueErrorResponse
503 Meshtastic radio problem. Different errors can be returned. application/json: RadioErrorResponse

[POST] /api/v1/integrations/apprise/channels/{channel_index}/messages

Apprise Gateway Adapter Send Text Channel

Adapter gateway.

Parameters

Name Located in Description Required Schema
channel_index path The channel index (0 to 7) Yes integer

Request Body

Required Schema
Yes application/json: AppriseJsonChannelBroadcastPayload

Responses

Code Description Schema
202 Successful Response application/json: MeshActionResponse
422 Validation Error application/json: HTTPValidationError
429 Unable to handle more requests because the FIFO queue is full application/json: QueueErrorResponse
503 Meshtastic radio problem. Different errors can be returned. application/json: RadioErrorResponse

[POST] /api/v1/integrations/apprise/nodes/{node_target}/messages

Apprise Gateway Adapter Send Text Node

Adapter gateway.

Parameters

Name Located in Description Required Schema
node_target path Target destination: can be a lowercase hex string NodeId (e.g. !2c3b4f5a) or a numeric NodeNum < 2^32 (e.g. 60). Yes string or integer

Request Body

Required Schema
Yes application/json: AppriseJsonNodeDirectPayload

Responses

Code Description Schema
202 Successful Response application/json: MeshActionResponse
422 Validation Error application/json: HTTPValidationError
429 Unable to handle more requests because the FIFO queue is full application/json: QueueErrorResponse
503 Meshtastic radio problem. Different errors can be returned. application/json: RadioErrorResponse

Schemas

AppriseJsonChannelBroadcastPayload Schema

Name Type Description Required
version string Apprise JSON schema version Yes
title string or null Unused parameter No
message string UTF-8 text limited to the 237 byte Meshtastic MTU (200 here for safety) Yes
type string,
Available values: "info", "warning", "success", "failure" or null
Unused parameter No
attachment [ ],
Default:
Unused parameter No
wantAck boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
portNum integer,
Default: 1
Protobuf application port number No

AppriseJsonNodeDirectPayload Schema

Name Type Description Required
version string Apprise JSON schema version Yes
title string or null Unused parameter No
message string UTF-8 text limited to the 237 byte Meshtastic MTU (200 here for safety) Yes
type string,
Available values: "info", "warning", "success", "failure" or null
Unused parameter No
attachment [ ],
Default:
Unused parameter No
wantAck boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
wantResponse boolean,
Default: true
true if you want the service on the other side to send an application layer response No
portNum integer,
Default: 1
Protobuf application port number No

ChannelBroadcastPayload Schema

Name Type Description Required
text string UTF-8 text limited to the 237 byte Meshtastic MTU (200 here for safety) Yes
wantAck boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
portNum integer,
Default: 1
Protobuf application port number No

HTTPValidationError Schema

Name Type Description Required
detail [ ValidationError ] No

MeshActionResponse Schema

Name Type Description Required
status string Always return "success" Yes
routing_mode string,
Available values: "broadcast", "direct"
Message routing type
Enum: "broadcast", "direct"
Yes
packet MeshPacketDetails Packet data from radio Yes
onResponse_callback_payload object or null No
truncated boolean Message was truncated to Meshtastic MTU before being sent No

MeshPacketDetails Schema

Name Type Description Required
id integer Yes
from integer or string Yes
to integer or string Yes
channel integer The channel index (0 to 7) Yes
portnum integer Protobuf application port number Yes
text string The message sent to the mesh Yes
wantAck boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
wantResponse boolean,
Default: true
true if you want the service on the other side to send an application layer response No

NodeDirectPayload Schema

Name Type Description Required
text string UTF-8 text limited to the 237 byte Meshtastic MTU (200 here for safety) Yes
wantAck boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
wantResponse boolean,
Default: true
true if you want the service on the other side to send an application layer response No
portNum integer,
Default: 1
Protobuf application port number No

QueueErrorResponse Schema

Name Type Description Required
detail string,
Default: Unable to handle more requests, queue full.
No

RadioErrorResponse Schema

Name Type Description Required
detail string,
Default: Meshtastic radio problem
No

ValidationError Schema

Name Type Description Required
loc [ string or integer ] Yes
msg string Yes
type string Yes
input No
ctx object No

Integrations

Apprise

restmesh accepts Apprise via the JSON schema. To be able to use it you need to install it first. See also the Repology page to see the available packages for Apprise.

[!NOTE] The title parameter is ignored! Write your full text in the body.

Channel

Simple example using channel 0:

apprise -b 'My message here' "json://localhost:8000/api/v1/integrations/apprise/channels/0/messages"

With parameters:

apprise -b 'Hello world!' "json://localhost:8000/api/v1/integrations/apprise/channels/0/messages?:wantAck=false&:portNum=1"

Node

Send a message to the node with hex id !0a1b2c3d. Alternatively you can use the decimal integer representation of the node id, without prepending the ! character:

apprise -b 'My message here' "json://localhost:8000/api/v1/integrations/apprise/nodes/!0a1b2c3d/messages"

apprise -b 'My message here' "json://localhost:8000/api/v1/integrations/apprise/nodes/169552957/messages"

With parameters:

apprise -b 'Hello world!' "json://localhost:8000/api/v1/integrations/apprise/nodes/!0a1b2c3d/messages?:wantResponse=true&wantAck=false&:portNum=1"

apprise -b 'Hello world!' "json://localhost:8000/api/v1/integrations/apprise/nodes/169552957/messages?:wantResponse=true&wantAck=false&:portNum=1"

Contributing

See Contributing.

Responsible usage policy

Meshtastic

In some places, such as Europe, the non-ham LoRa band has limited air time use. Please don't use restmesh for mass spamming, and never broadcast automated messages on public channels such as MediumFast or LongFast. Setup private channels instead.

restmesh has a basic message FIFO queue also to mitigate the air time problem.

Consulting and custom integrations

If you need help or custom endpoints and integrations, I'm available for contract-based freelance consulting and custom Python development:

License

Copyright (C) 2026 Franco Masotti

restmesh is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

restmesh is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with restmesh. If not, see http://www.gnu.org/licenses/.

Changelog and trusted source

You can check the authenticity of new releases using my public key.

Changelogs, instructions, sources and keys can be found at blog.franco.net.eu.org/software/#restmesh.

Git forge mirrors

URL Type Notes
https://github.com/frnmst/restmesh RW Official home
https://codeberg.org/frnmst/restmesh RW Mirror
https://framagit.org/frnmst/restmesh RW Mirror
https://repos.franco.net.eu.org/frnmst/restmesh RW Mirror

Support this project

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

restmesh-0.2.0.tar.gz (30.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

restmesh-0.2.0-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

Details for the file restmesh-0.2.0.tar.gz.

File metadata

  • Download URL: restmesh-0.2.0.tar.gz
  • Upload date:
  • Size: 30.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for restmesh-0.2.0.tar.gz
Algorithm Hash digest
SHA256 343619daa7df48ae7d7826c4f388a9b6d30424e5d8786656a2cfb5280cdd577c
MD5 b8fa74df56b14700e70806f60b850af4
BLAKE2b-256 da50e8a4bf28f34bbc217beca8666bac772607939cc7541b40d18bc011f6cd8d

See more details on using hashes here.

File details

Details for the file restmesh-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: restmesh-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 26.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for restmesh-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f3a1705baefa746d9cfc48f4de25b5d1e3887b2ec10301fa4f36ab9259abe34b
MD5 7f5d1bf43b3a9fa92b3e74e739da025f
BLAKE2b-256 93e2d9b6aac31cd93ad149af18956cb26323dde39599d4b0dee7232a30b59dc1

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page