Presses and private cards
A press
Section titled “A press”When a person presses an action on your wisp’s card, chooses from a menu, or presses a grid
cell, their app calls act. Woosh checks that the card offers that action with that value (or,
for a menu, that choice), that it is not switched off, that they can see the channel, that your
wisp is still there with that channel granted, and that they are within their own 3 runs and
presses a second. Then your line hears act:
{ "c": 61, "n": 111, "t": "act", "d": { "server": "5c3b1a2d-8e4f-4a6b-9c7d-0e1f2a3b4c55", "channel": "6d4c2b3e-9f5a-4b7c-8d6e-1f2a3b4c5d66", "message": "0199dea4-1d31-7b4c-9d5e-6f7a8b9c0d21", "id": "0199dea4-2e42-7c5d-ae6f-7a8b9c0d1e32", "user": "7a2e4c19-0b3d-4f6a-8e25-91c3d5f0a622", "name": "bo", "avatar": "fox", "action": "spin", "expires": 1791916262000000 }}| Key | |
|---|---|
server, channel |
where |
message |
the line pressed, or a private card’s id |
id |
the press: what you answer |
user, name?, avatar? |
who pressed |
action |
your action’s name |
value? |
a menu’s choice, or the value you put on the action or cell |
on? |
a tally that tells you: true while the person is counted, left out once they took it back |
expires |
when your chance to answer ends, 15 seconds after the press |
caio picks a game from the card’s menu:
{"c":61,"n":112,"t":"act","d":{"server":"5c3b1a2d-8e4f-4a6b-9c7d-0e1f2a3b4c55","channel":"6d4c2b3e-9f5a-4b7c-8d6e-1f2a3b4c5d66","message":"0199dea4-1d31-7b4c-9d5e-6f7a8b9c0d21","id":"0199dea4-2f13-7c6d-8e7f-8a9b0c1d2e43","user":"b84d0e37-5c2a-4e91-a6f3-0d7c8e1b9433","name":"caio","action":"game","value":"blackjack","expires":1791916270000000}}A press Woosh refuses never reaches your wisp, and the person’s app says it did not go through. The refusals you will meet most:
| Refused with | Why |
|---|---|
| “That is switched off” | the action is disabled on the card as it shows now |
| “That is not one of the menu’s choices.” | a choice the menu does not offer |
| “The card offers no such press.” | a value the card does not carry |
| “This card is gone” | a private card Woosh no longer knows, or someone else’s |
limited |
past their own 3 runs and presses a second: their app asks them to try again in a moment |
A link is never pressed: the app opens it, and nobody hears it.
Answering a press
Section titled “Answering a press”A press is answered once, before expires, with respond, as a run is
(Answering covers retries and refusals). Until then the person’s app
shows your wisp working on it under the card; with no answer by expires, it says your wisp did
not answer. Three ways:
respond with |
What happens | v |
|---|---|---|
edit: true |
the pressed line itself changes, as an edit would: the game’s card moves on. Send text too, which replaces the line’s, and a card or a patch. Spends that line’s edits |
the pressed line |
private: true |
an aside to the person who pressed, under the press’s id | the press’s id |
| neither | a new line in the channel, a reply to the pressed one, with text |
the new line |
caio picked Blackjack, so the card moves on in place:
{ "i": 68, "t": "respond", "d": { "to": "0199dea4-2f13-7c6d-8e7f-8a9b0c1d2e43", "text": "Blackjack: caio draws 17.", "card": { "color": "#22c55e", "blocks": [ { "type": "text", "text": "**Blackjack**", "size": "large" }, { "type": "text", "text": "caio draws 17." }, { "type": "actions", "actions": [ { "action": "hit", "label": "Hit", "kind": "press", "tone": "accent" }, { "action": "stand", "label": "Stand", "kind": "press" } ] } ] }, "edit": true }}{"r":68,"v":"0199dea4-1d31-7b4c-9d5e-6f7a8b9c0d21"}v is the line that changed. The people looking hear message.edit with the whole card and the
press it answers as its run, which ends the wait in caio’s app.
Switching actions off while you work
Section titled “Switching actions off while you work”A spin takes a moment. Switch the card’s actions off ("disabled": true) while it turns, and
on again with the result, so nobody presses twice at once. The quickest way is a frame, here on
a card whose actions row carries the key buttons; nothing is stored, and the answer that
follows is the one edit:
{ "i": 67, "t": "card.live", "d": { "message": "0199dea4-1d31-7b4c-9d5e-6f7a8b9c0d21", "patch": { "buttons": { "type": "actions", "actions": [{ "action": "spin", "label": "Spin", "kind": "press", "tone": "accent", "disabled": true }] } } }}and answer the press with the result, the actions on again (Cards has the whole spin).
The apps draw a switched-off action and do not let it be pressed. Woosh checks every press against what the card offers now: its last frame (for up to ten minutes), or else its last edit. After the frame above, a press of Spin is refused (“That is switched off”), even from an app that opened the channel later and still draws the stored card, until your answer switches it on again. A press can still slip in before the frame lands: answer it with a short private word (“Still spinning”).
Live frames
Section titled “Live frames”card.live replaces a card in place for the people looking at the channel, between edits. It
is never stored: whoever opens the channel later sees the line as its last edit left it, while
its presses are checked against the frame. It is a call like any other, sent with i and
answered with an ack:
{"r":67}d |
|
|---|---|
message |
your own line with a card |
to |
instead of message, for a private card: the press on it you are working on |
card or patch |
the whole card, or what changes. A private card’s frame is always a whole card |
The people looking hear card.live with the line’s id, its channel, and the card or the
patch you sent. A whole card replaces the one on screen; a patch is merged into it. A patch on
a line with no card is refused (“The line has no card to patch.”).
Send a frame in its channel’s order, as an edit (The line), so it never lands after the edit that follows it.
A frame spends one of the line’s edits, 4 a second shared with its edits, and one of the
server’s calls; a private card’s frame spends only that card’s edits. A frame past them answers
limited with wait, and nobody saw it. Rather than send that one again, keep only the newest
frame and send it once the wait is over (Budgets and limits).
Most motion needs no frames at all: an animated picture
turns a reel for free.
Private cards
Section titled “Private cards”An aside may carry a card: a game only its player sees, a settings panel, a shop. Answer a run
with private and a card:
{ "i": 70, "t": "respond", "d": { "to": "0199dea4-4a10-7b2c-8d3e-4f5a6b7c8d01", "text": "bo sat down at the slots.", "card": { "color": "#f97316", "blocks": [ { "type": "text", "text": "**Your slots**", "size": "large" }, { "type": "grid", "rows": [[{ "emoji": "❔" }, { "emoji": "❔" }, { "emoji": "❔" }]] }, { "type": "fields", "fields": [{ "name": "Bet", "value": "10" }, { "name": "Coins", "value": "1,000" }] }, { "type": "actions", "actions": [ { "action": "spin", "label": "Spin", "kind": "press", "tone": "accent" }, { "action": "cashout", "label": "Cash out", "kind": "press", "tone": "bad" } ] } ] }, "private": true }}{"r":70,"v":"0199dea4-4a10-7b2c-8d3e-4f5a6b7c8d01"}v is the private card’s id, which is the run’s own id: an aside goes under the run it
answers. bo’s apps hear it as an aside:
{"t":"aside","d":{"id":"0199dea4-4a10-7b2c-8d3e-4f5a6b7c8d01","channel":"6d4c2b3e-9f5a-4b7c-8d6e-1f2a3b4c5d66","author":"d2b6f4a8-3e1c-4b7d-9a05-6c8e0f2a4b19","name":"lucky","avatar":"639201499012345678","to":"0199dea4-4a10-7b2c-8d3e-4f5a6b7c8d01","text":"bo sat down at the slots.","card":{…},"at":1791916301000000}}A private card is kept nowhere: not stored with the channel, not in any log. Only bo’s apps that are open have it, and it is gone when he reloads. So:
- Keep its state yourself, by its id (the
vabove), or in the values on its actions. - It has no tally: there is one person to count. A private card with a
tallyis refused. - A press on it names your wisp and the channel. bo’s app calls
actwith the card’s id asmessage, andwispandchannelbeside it. Your line hearsactas for any press, withmessagethe private card’s id. - Woosh checks its presses as a line’s. It keeps what the card offers to press, never what it says, for an hour from when it was last sent, moved or pressed. A press it does not offer is refused as on a line, and one switched off with “That is switched off”. A press by anyone but bo, in another channel, or after that hour is refused with “This card is gone”.
- A press on a card you no longer know (your wisp restarted and lost its state) deserves a short private answer: “This machine is gone. /spin for a new one.”
- It moves on for its person alone. Answer a press on it with
edit, and the aside is replaced in place under the same id, for bo only, kept nowhere again. Itstonames the new press. Each move spends one of that card’s edits, 4 a second. - Frames go to its person alone, always whole. Send
card.livewithto, the press you are working on (one from the last minute), instead ofmessage, and the wholecard: Woosh keeps no card of his to patch. bo’s apps hearcard.livewith the card’sid, and its presses are checked against the frame, as on a line.
{"i":72,"t":"card.live","d":{"to":"0199dea4-4b21-7c3d-9e4f-5a6b7c8d9e02","card":{"color":"#f97316","blocks":[{"type":"text","text":"**Your slots**","size":"large"},{"type":"grid","rows":[[{"emoji":"❔"},{"emoji":"❔"},{"emoji":"❔"}]]},{"type":"fields","fields":[{"name":"Bet","value":"10"},{"name":"Coins","value":"1,000"}]},{"type":"actions","actions":[{"action":"spin","label":"Spin","kind":"press","tone":"accent","disabled":true},{"action":"cashout","label":"Cash out","kind":"press","tone":"bad","disabled":true}]}]}}}A frame whose to is not a press on one of your private cards from the last minute is refused
with “No such run or press”.
Answer a press on a private card with edit or private. A plain respond posts a line in the
channel for everyone, which a private game rarely wants. To share a result, a Share button that
sends one line of its own does it well.
Tallies
Section titled “Tallies”A tally is a button Woosh counts by itself, kept as reactions are: one press a person, a
second press takes it back, and the count is drawn on the card within a second, at most once a
second, however many press. A giveaway with 5,000 entries in 10 seconds costs your wisp nothing.
{ "i": 12, "t": "send", "d": { "channel": "6d4c2b3e-9f5a-4b7c-8d6e-1f2a3b4c5d66", "text": "Win a monitor. Press Enter by Sunday 20:00.", "sendId": "a5b6c7d8-e9fa-4b0c-82d3-e4f5a6b7c8d9", "card": { "blocks": [ { "type": "text", "text": "**Win a monitor**", "size": "large" }, { "type": "media", "url": "<media>/wisps/d2b6f4a8-3e1c-4b7d-9a05-6c8e0f2a4b19/7c8d9eaf-b0c1-4d2e-8f30-4a5b6c7d8e93", "alt": "A 27 inch monitor" }, { "type": "text", "text": "Press Enter by {at:1792353600000000}.", "size": "small" }, { "type": "actions", "actions": [{ "action": "enter", "label": "Enter", "kind": "tally", "tone": "accent" }] } ] } }}- The count is Woosh’s. Whatever
countyou write is replaced with the real one the next time it is drawn, and left out while nobody is counted. It is kept with the line, so whoever opens the channel later sees it. An edit that keeps the tally’s name keeps its count. - A card’s tallies have names of their own, since each is counted by its name. A tally
carries no
value. - A count shows the card as stored. Each time it is drawn, the people looking get the card as your last edit left it, counted, and presses are checked against that again. Move a tally’s card on with edits rather than frames.
- Hearing each press is a choice. Without
tell, your wisp hears nothing. With"tell": true, every press and take-back is anactwithon(counted) or without it (taken back). You may answer it as any press (a private “You’re in”), but you need not: the person’s app waits on no answer to a tally, since the count is the answer. - Read who it counts when you need them with
tally, a call on the line, up to a thousand at a time in the order of their ids:
{"i":90,"t":"tally","d":{"message":"0199dea4-3064-7e7f-8081-9c0d1e2f3a54","action":"enter","limit":1000}}{"r":90,"v":[{"user":"3f1c9a52-6d1e-4c8b-9a51-2b7e0c4d8f11","at":1791916201150000},{"user":"7a2e4c19-0b3d-4f6a-8e25-91c3d5f0a622","at":1791916203410000}]}at is when they pressed. limit is 1 to 1,000, and 1,000 when left out. For the next page,
pass the last user as after; a page shorter than limit is the last.
- Or let Woosh draw.
drawpicks up tocount(1 to 100) of the people a tally counts, at random, so no list need leave Woosh. Fewer come back when it counts fewer:
{"i":92,"t":"draw","d":{"message":"0199dea4-3064-7e7f-8081-9c0d1e2f3a54","action":"enter","count":1}}{"r":92,"v":["7a2e4c19-0b3d-4f6a-8e25-91c3d5f0a622"]}Both read only your wisp’s own lines, and each spends one of the server’s calls.
© 2026 Woosh AS. All rights reserved.