Reference
Keys marked ? may be left out; a key left out is its default (no value, false, an empty list,
an empty text, 0). Ids are lowercase GUIDs; times are microseconds since 1970, UTC.
Frames Woosh sends
Section titled “Frames Woosh sends”| Frame | Shape | |
|---|---|---|
| ready | {"t":"ready","v":2,"id","s","g?","e?","replay","silence","retry","media","gateway?","slices","budget"} |
The line |
| numbered event | {"c":<group>,"n":<number>,"t":<name>,"d":{…}} |
Frames |
| unnumbered event | {"t":<name>,"d":{…}} |
|
| answer | {"r":<i>} or {"r":<i>,"v":…} |
Calls and answers |
| error | {"r":<i>,"e":<code>,"w":<words>}, and "wait":<ms> with limited |
|
| ping | {"t":"ping"} |
after 15 seconds of nothing else |
| move | {"t":"move"} |
Moving |
Frames a wisp sends
Section titled “Frames a wisp sends”| Frame | Shape | |
|---|---|---|
| hello | {"t":"hello","v":2,"r?","e?","a","f":1,"slice?"} |
The line |
| call | {"i":<number>,"t":<name>,"d":{…}} |
i from 1 to 2,147,483,647; one frame a message, at most 64 KB |
| moved | {"t":"moved"} |
Moving |
Events a wisp hears
Section titled “Events a wisp hears”All numbered, on the wisp’s own group.
| Event | Grant | d |
|
|---|---|---|---|
command |
always | server, channel, id, user, name?, avatar?, command, options?, expires |
Commands |
act |
always | server, channel, message, id, user, name?, avatar?, action, value?, on?, expires |
Presses |
server |
always (new, grants); shape for other changes |
id, name, icon?, owner, unread?, new?, grants?: hears?, does?, channels? |
Always |
server.gone |
always | id, why (removed, kicked, banned, deleted), name? |
Always |
channel |
shape |
id, server, name, type?, new? |
shape |
channel.gone |
shape |
id, server |
|
layout |
shape |
server, nodes? |
|
roles |
shape |
server, roles |
|
emoji |
shape |
id, server, name, url, serverName |
|
emoji.gone |
shape |
id, server |
|
member |
members |
server, user, name?, avatar?, avatarColor?, banner?, bannerColor?, status?, flags?, joined, roleIds? |
members |
member.roles |
members |
server, user, roleIds? |
|
member.gone |
members |
server, user |
|
message |
messages |
server, id, channel, parent?, author, name?, avatar?, text?, at, edited?, reply?, files?, imported?, card?, run? |
messages |
message.edit |
messages |
server, id, channel, parent?, text?, files?, edited?, card?, run? |
|
message.gone |
messages |
server, id, channel, parent? |
|
reaction |
messages |
server, message, channel, parent?, emoji, user, name?, on? |
A line’s parts: reply is id, author?, name?, text?; each of files (1 to 10) is id,
url, name?, type?, mb?, w?, h?, expires?; run is id, user, name?,
command? (left out on an answer to a press); imported is the name the line’s author had in
the app it was brought over from.
Calls a wisp makes
Section titled “Calls a wisp makes”| Call | d |
Answer | |
|---|---|---|---|
respond |
to (a run or a press), text? (up to 4,000), card? (or patch? with edit on a line), private?, edit? (a press only) |
v: the line’s id, the aside’s (the run’s own id), or the pressed line’s |
Commands, Presses |
send |
channel, text, card?, replyTo?, parent?, sendId |
v: the line’s id |
send |
edit |
message, text, card? or patch? |
ack | edit |
card.live |
message (its own line with a card) and card or patch; or to (a press on a private card, from the last minute) and a whole card |
ack | Live frames |
delete |
message |
ack | delete |
react |
message, emoji, parent?, on? |
ack | react |
roles |
server, user, add?, remove? |
ack | roles |
kick |
server, user |
ack | kick and ban |
ban |
server, user |
ack | kick and ban |
activity |
activity? (your wisp’s words, up to 1,024 characters; left out clears them) |
ack; one call every 15 seconds, and the words last two minutes | Your wisp’s words |
history |
channel (where it hears messages), before? or after? (a time), limit? (1 to 100) |
v: the lines, newest first, each as it hears message; thread replies not included |
Catching up |
members |
server (where it hears members), offset?, limit? (1 to 1,000) |
v: the members in the order the apps draw them, each as it hears member |
|
member |
server (where it hears members), user |
v: the member, as it hears member |
|
tally |
message (its own line), action, after? (a person’s id), limit? (1 to 1,000; 1,000 when left out) |
v: each counted person’s user and at, in the order of their ids |
Tallies |
draw |
message (its own line), action, count (1 to 100) |
v: up to count people’s ids, picked at random |
A wisp does not send presence (Woosh keeps it for you), nor typing, read, view or follow, which are a person’s.
Error codes
Section titled “Error codes”e |
Means | |
|---|---|---|
refused |
Woosh said no; w says why. Do not retry |
also any code you do not know |
limited |
past a budget; nothing done. Again after wait ms |
Budgets and limits |
failed |
it may or may not have run. Retry what is safe to repeat |
The words in w are for your log and may change. The ones you will meet most: “Not granted”,
“Already answered”, “Too late”, “No such run or press”, “That is switched off”. A check a person
would meet too answers with a person’s words, such as “Missing permission: KickMembers.” or “The
server owner cannot be kicked.”.
Close codes
Section titled “Close codes”| Code | What to do | |
|---|---|---|
| 4426 | version |
stop |
| 4401 | unauthorized |
a new token, then again at once; if a second 4401 follows, back off |
| 4403 | revoked |
stop |
| 4429 | retry=<s> |
wait the seconds named |
| 1013 | retry=<s> |
wait the seconds named |
| 1012 | restart |
come back at once |
| 1011 | connect failed |
back off; check the slice |
| 1000 | moved |
nothing |
The details are on The line.
What people’s apps hear
Section titled “What people’s apps hear”Not on your line, but what your calls become:
| Frame | To | |
|---|---|---|
message with card and run |
the people looking at the channel | your line, with who ran which command above it |
aside |
the person alone, unnumbered | id, channel, author (your wisp), name?, avatar?, to?, text?, card?, at: a private answer, kept nowhere; the same id again replaces it |
card.live |
the people looking, unnumbered | id, channel, parent?, card or patch: a frame of your card |
commands |
the server | server, at: its commands changed. Your wisp does not hear it |
https://api.woosh.no, with Authorization: Bearer <token>. JSON keys in camelCase.
| Route | Who | |
|---|---|---|
POST /api/auth/login |
you | username (or your email) and password: answers your own token |
POST /api/oauth/token |
the wisp, with its id and secret | an hour’s token, and the line to open. At most 120 requests a minute from one address. Refusals: 400 unsupported_grant_type, 401 invalid_client, 429 |
POST /api/wisps |
its owner | make one: answers the wisp and its secret, shown once |
GET /api/wisps |
its owner | your wisps |
PATCH /api/wisps/{id} |
its owner | change its name, description, asks or slices |
POST /api/wisps/{id}/secret |
its owner | a new secret; the old one’s lines close with 4403 |
PUT /api/wisps/{id}/commands |
its owner, with their own token (a wisp’s cannot) | its commands, replaced whole |
GET /api/wisps/{id} |
anyone signed in | what the approval page shows: who made it, its reach, whether it was reviewed, all it asks for |
POST /api/servers/{id}/wisps |
an Administrator | add a wisp with what is approved: answers the wisp as the server holds it, with asksMore |
GET /api/servers/{id}/wisps |
an Administrator | the server’s wisps, each with what it asks for beyond its grant |
PATCH /api/servers/{id}/wisps/{wisp} |
an Administrator | change a grant |
DELETE /api/servers/{id}/wisps/{wisp} |
an Administrator | take a wisp out: answers 204 |
GET /api/servers/{id}/commands |
any member | the server’s commands, for the picker: each wisp’s wisp, name, avatarUrl, channels (null: every channel) and commands |
POST /api/wisps/files |
the wisp, with its own token | a picture for its cards: answers its url (Pictures) |
A wisp’s token reaches only POST /api/oauth/token and POST /api/wisps/files. Everything else
it does and reads is a call on its line. A REST refusal answers 403, 404 or 409 with
{"error": "<words>", "traceId": "…"}. A body Woosh cannot read answers 400 (or 415 for the
token route sent as something other than a form), a wrong sign-in 401, and too many requests
429. The token route answers {"error": "invalid_client"} or {"error": "unsupported_grant_type"}, and a picture past two a second answers 429 with Retry-After.
Card blocks
Section titled “Card blocks”A card is color? (#rrggbb) and blocks: at most 40 parts, 4,000 characters and 8 KB.
| Block | Holds |
|---|---|
text |
text (with marks), size?, tone? |
fields |
fields: 1 to 10 of name (1 to 256 characters) and value (1 to 1,024) |
grid |
rows: 1 to 5 rows of 1 to 5 cells, each one of text, emoji, or url and alt; tone?; action? with value? and disabled? |
media |
url, alt, size? (small, normal or large) |
gallery |
items: 2 to 10 of url, alt |
section |
text, and media or action beside it (not both), tone? |
progress |
value, max, label?, tone? |
actions |
actions: 1 to 5 |
pages |
pages: 2 to 10 of label, blocks |
divider |
nothing |
Any block or cell may carry a key for patches. The whole model is on Cards.
© 2026 Woosh AS. All rights reserved.