Skip to main content

restmesh

PyPI restmesh version Downloads Buy me a coffee M-Powered FastAPI

A stateless thread-safe REST API for Meshtastic.

image

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

Examples

Error reporting

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] [--version] [--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
  --version             show program's version number 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!",
  "want_ack": false,
  "port_num": 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",
  "want_ack": false,
  "want_response": true,
  "port_num": 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
want_ack boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
port_num 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
want_ack boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
want_response boolean,
Default: true
true if you want the service on the other side to send an application layer response No
port_num 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
want_ack boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
port_num 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 radios Yes
on_response_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
port_num integer Protobuf application port number Yes
text string The message sent to the mesh Yes
want_ack boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
want_response 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
want_ack boolean true if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) No
want_response boolean,
Default: true
true if you want the service on the other side to send an application layer response No
port_num 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.3.0.tar.gz (31.8 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.3.0-py3-none-any.whl (26.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for restmesh-0.3.0.tar.gz
Algorithm Hash digest
SHA256 850c0ab20d51b88f1e87f528767320d789150fd0b3ec9abe4f8e6d9c43fa6c8d
MD5 0cdf55715869bce3f1b62b2392c986b5
BLAKE2b-256 5cda0286225e399c1ba604f79a417f6d25e26dacc9d0fdaa4bde238d40f9ac3f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: restmesh-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 26.6 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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ef447e09569934cb0e9121a66980b560c476858457bad02495ca5a8b9ddc9b7c
MD5 ef5e9eff64ce5ec06deda6ef28034ce7
BLAKE2b-256 706096b4d6f4cbedaf0b836cf51e02103087dfe79ade4f9bcf3d47f6870c7410

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

This release

0.3.0 This release

2 files

0.2.0

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