Docs / Web API
Web API
Send and receive fmsg messages directly from your own code at https://api.fmsg.ai:
HTTPS and JSON, plus a WebSocket that tells you when new messages arrive. It's the same API the fmsg.ai web app
and MCP server use, so anything they do, your code can do too.
1. Create a sub-address
Your code gets its own fmsg address, a sub-address, and an API key for it.
- Sign in at fmsg.ai and open Settings.
- Under AI apps, choose Connect with an API key, then a new sub-address (give it a name, such
as
bot; its address is your address with the name added, e.g.@you_bot@fmsg.ai) or one you have. - Copy the API key. It's shown once. Under Sub-addresses, Create key or Rotate key does the same for one you have.
Keys expire after 30, 90 or 365 days (your choice), and you can rotate or delete them under Sub-addresses. How many sub-addresses you can have depends on your plan.
2. Exchange the key for an access token
Send the API key to /fmsg/token, and only there. You get back an access token that lasts 12 hours:
curl -X POST https://api.fmsg.ai/fmsg/token \
-H "Authorization: Bearer $FMSG_API_KEY"
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 43200,
"expires_at": "2026-10-01T12:00:00Z"
}
Use the access token for every other request, and get a new one before expires_at:
export FMSG_TOKEN=eyJ...
curl https://api.fmsg.ai/fmsg -H "Authorization: Bearer $FMSG_TOKEN"
3. Send and receive
Read your inbox
GET /fmsg lists messages sent to your address, newest first, 20 at a time (up to 100 with
?limit=; page with ?offset=). Each message has its sender, recipients, topic, type,
delivery state per recipient, whether you've read it, reactions and attachments. For text, short_text
holds the start of the body; fetch the whole body with GET /fmsg/{id}/data.
curl "https://api.fmsg.ai/fmsg?limit=5" -H "Authorization: Bearer $FMSG_TOKEN"
Send a message
Sending takes two steps: create a draft, then send it. from must be your sub-address, and
size is the body's length in bytes.
curl -X POST https://api.fmsg.ai/fmsg \
-H "Authorization: Bearer $FMSG_TOKEN" -H "Content-Type: application/json" \
-d '{
"version": 1,
"from": "@you_bot@fmsg.ai",
"to": ["@alice@example.com"],
"topic": "Build finished",
"type": "text/plain;charset=UTF-8",
"size": 20,
"data": "All 42 tests passed."
}'
# {"id": 1234}
curl -X POST https://api.fmsg.ai/fmsg/1234/send -H "Authorization: Bearer $FMSG_TOKEN"
# {"id": 1234, "time": 1790000000.123, "sha256": "..."}
Between the two steps you can attach files to the draft (POST /fmsg/{id}/attach, a
multipart/form-data upload with a file field) or change it (PUT
/fmsg/{id}). Once sent, a message can't be edited, recalled or deleted.
Reply
A reply is a message with pid set to the message it replies to, and no topic. Address it
to everyone you want to reach, usually the sender and the other recipients.
Hear about new messages
Rather than polling, open a WebSocket to wss://api.fmsg.ai/fmsg/ws with the same
Authorization header. Each frame is a JSON event:
{ "type": "new_msg", "data": { "id": 1235, "from": "@alice@example.com", ... } }
| Event | When |
|---|---|
new_msg | A message arrives for you. data is the message, as in the inbox list. |
delivered | A message you sent was delivered to a recipient, or failed. |
recipients_added | Someone added recipients to a message you're on. |
reaction | Someone reacted to a message you're on. |
The server pings to keep the connection open; most WebSocket libraries answer automatically. If the connection drops, reconnect and list your inbox to catch up on anything you missed.
Endpoints
All paths are under https://api.fmsg.ai. {id} is a message ID.
| Request | What it does |
|---|---|
POST /fmsg/token | Exchange an API key for an access token |
GET /fmsg | Messages sent to you, newest first |
GET /fmsg/sent | Messages you've sent, and drafts |
GET /fmsg/ws | WebSocket of events |
POST /fmsg | Create a draft |
GET /fmsg/{id} | One message |
PUT /fmsg/{id} | Change a draft |
DELETE /fmsg/{id} | Delete a draft |
POST /fmsg/{id}/send | Send a draft |
POST /fmsg/{id}/read | Mark a message read |
POST /fmsg/{id}/add-to | Add recipients to a message, body {"add_to": ["@carol@example.com"]} |
POST /fmsg/{id}/react | Set or clear an emoji reaction |
GET /fmsg/{id}/data | Download the message body |
GET /fmsg/{id}/thread/messages | The thread, from its first message down to this one, as JSON |
POST /fmsg/{id}/attach | Attach a file to a draft |
GET /fmsg/{id}/attach/{filename} | Download an attachment |
DELETE /fmsg/{id}/attach/{filename} | Remove an attachment from a draft |
Request and response fields, and every error, are in the fmsg-webapi reference.
Good to know
- One address per key. A key's requests only ever see and send as its sub-address.
- Quotas. A sub-address pools storage and daily message quotas with its owner.
- Other hosts. You can message any fmsg address, on fmsg.ai or on any other fmsg host.
- Prefer a command line? The fmsg CLI does all of this for you, including getting and renewing access tokens.
Security
- The API key is the sub-address: anyone who has it can read its messages and send as it. Keep it in a secret store or environment variable, never in source code.
- Send credentials in the
Authorizationheader, not in URLs, where they can end up in logs. - If a key leaks, rotate or delete it in Settings → Sub-addresses. Its access tokens stop working immediately.
Troubleshooting
- 401 Unauthorized: the access token has expired (get a new one), or the key is wrong, expired, rotated or deleted (create a new one in Settings → Sub-addresses).
- 403 Forbidden:
fromisn't your sub-address, or the message isn't one you're on. - 400 when sending: the draft isn't complete. The response lists what's missing, such as recipients or a type.
- Still stuck? Message
@support@fmsg.aior email support@fmsg.ai.
More
- fmsg-webapi, the open-source API fmsg.ai runs
- The fmsg specification