The commands and events available to bots (through bot.call(cmd, …) in the SDKs) and to app integrations.
Transport
Lumora Bot Runner reads one JSON request per line and writes responses and events as JSON lines.
The bot SDKs (Python, JavaScript) start
the Bot Runner for you and expose bot.call(cmd, **args) / bot.call(cmd, args) for any command below.
Request / response
→ {"id": 7, "cmd": "send_message", "conv": "d:<peer>", "text": "hi"}
← {"id": 7, "ok": true, "data": { ... }}
← {"id": 7, "ok": false, "error": "human readable"}
The top-level id is always the request id; command arguments never use the key id.
Identifiers. Peer ids are 52-character lowercase strings. short_id is the 9-digit ID shown to users ("123 456 789").
Usernames are shown as @name·tag: the tag belongs to the account, so a handle cannot be impersonated. Bot usernames end with bot.
Conversation ids: d:<peer> direct chat, g:<gid hex32> group/community channel, s:me saved messages.
Message, file, call, session and story ids are 32 hex chars. Timestamps are ms since epoch.
Commands
Identity & profile
| cmd |
args |
data |
ping |
– |
"pong" |
me |
– |
{peer, short_id, name, username, handle, protection, version} |
set_name |
name |
– |
profile |
– |
{name, bio, status, avatar} |
set_profile |
name?, bio?, status?: "online"|"away"|"dnd"|"invisible", avatar?: base64 (≤ 96 KB) |
profile |
set_cover |
cover (base64 image) |
profile |
set_username |
username (3–24 chars a–z 0–9 _, not starting with _; empty = remove) |
{username, handle} |
resolve_username |
handle (@name or @name·tag) |
[{peer, handle, name?, contact}] |
recovery_phrase |
– |
{phrase} |
change_passphrase |
old?, new? |
{protection} |
export_backup |
path, password (≥ 6), passphrase? |
{path, bytes} (restore it when setting up a new device) |
set_lang |
lang: "vi"|"en" |
{lang} |
i18n_catalog |
– |
{en:{key:text}, vi:{…}}. System messages are stored as body:{code,args,text}; render code with this catalog |
| cmd |
args |
data |
contacts |
– |
[Contact] |
add_contact |
target (peer, 9-digit short id or @name·tag), note? |
{peer} (sends a friend request) |
lookup |
short_id |
[{peer, name}] |
accept_contact / remove_contact / block_contact / unblock_contact |
peer |
– |
rename_contact |
peer, alias |
– |
verify_contact |
peer |
{safety_number, verified} |
mark_verified |
peer, verified: bool |
– |
contact_avatar / contact_cover |
peer |
{avatar} / {cover} (base64 or null) |
Conversations & messages
| cmd |
args |
data |
conversations |
– |
[Conversation] |
messages |
conv, before?: hlc, limit?: 50 |
[Message] (oldest → newest) |
send_text |
conv, text, reply_to?, ttl_ms? |
Message |
send_message |
conv, text, reply_to?, ttl_ms?, mentions?: [peer], mention_all?, buttons?: [[Button]] |
Message (receivers get a mention event) |
edit_message |
conv, msg_id, text |
– |
delete_message |
conv, msg_id, for_everyone?: true |
– (false = delete for me only) |
react |
conv, msg_id, emoji, on |
– |
forward_message |
msg_id, to_conv |
Message (meta.forwarded_from) |
pin_message / pinned_messages |
conv, msg_id, on / conv |
– / [Message] |
send_poll / vote |
conv, question, options[2..10] / conv, msg_id, option |
Message / – (votes in reactions.__votes) |
schedule_message / scheduled_messages / cancel_scheduled |
conv, text, at / – / sched_id |
– |
send_location |
conv, lat, lon, accuracy_m?, label?, live_minutes? |
Message (live_minutes > 0 = live location) |
update_live_location |
conv, msg_id, lat, lon, accuracy_m?, stop? |
– |
send_voice |
conv, path, duration_ms, wave?: [0..255] |
Message |
send_contact |
conv, peer |
Message (contact card) |
mark_read |
conv |
– |
typing |
conv, on |
– |
seen_by |
msg_id |
readers of a group message |
search |
q |
[Message] |
conv_flag |
conv, flag: "muted"|"pinned"|"archived", value |
– |
mute |
conv, until? (ms timestamp; 0 = unmute; omitted = forever) |
{muted, until} |
set_retention / get_retention |
conv, mode: "forever"|"session"|"24h"|"7d"|"30d" / conv |
– / {mode} |
clear_history |
conv, for_everyone?: false |
– |
delete_conversation |
conv, for_everyone?: false |
– |
set_wallpaper / get_wallpaper |
conv (or "default"), wallpaper / conv |
– / {wallpaper} |
Bots
| cmd |
args |
data |
set_bot |
commands: [{command, description}], about?, auto_accept?: true |
– (turns this identity into a bot; clients show the command menu) |
send_message |
…, buttons: [[{text, data?, url?}]] |
Message (max 8×8 buttons; text/data ≤ 64 chars) |
press_button |
msg_id, data |
– (client side: delivers a callback event to the message author) |
Stickers
| cmd |
args |
data |
send_sticker |
conv, hash (custom) or conv, pack, sticker (built-in) |
Message |
sticker_packs |
– |
[pack] |
sticker_pack_create / sticker_pack_delete |
name / pack |
pack / – |
sticker_add |
pack, path, emoji? (PNG/WebP/GIF, animated supported) |
sticker |
sticker_remove / sticker_save |
hash, pack |
– |
sticker_get |
hash |
{mime, data} (base64) |
sticker_pack_export / sticker_pack_import |
pack, path / path |
result |
Stories (24 h)
| cmd |
args |
data |
post_story |
text?, image_path?, bg?: "aurora" |
story |
stories |
– |
[story] |
story_image |
story_id |
{image} (base64) |
story_seen / story_viewers |
story_id |
– / viewers |
Groups & communities
| cmd |
args |
data |
create_group |
name, members[], kind?: 0 (0 group, 1 community with channels) |
{gid, conv} |
groups / spaces |
– |
groups / communities with channels |
group_info |
gid |
{gid, name, owner, members:[{peer,name,role}], epoch, my_role, …} |
group_add / group_remove |
gid, peer |
– |
group_set_role |
gid, peer, role: "admin"|"member" |
– |
group_rename |
gid, name |
– |
group_meta / group_avatar |
gid, avatar?, description? / gid |
– / {avatar} |
group_channel |
gid, name, voice?, remove? |
– |
group_settings |
gid, slow_mode_s?, admins_only? (announcement channel) |
– |
group_ban |
gid, peer, ban?: true |
– |
group_purge_member |
gid, peer |
– (delete a member's messages) |
group_clear_history |
gid |
– |
group_leave |
gid |
– |
create_invite / inspect_invite / join_invite |
gid, hours?: 168 / link / link |
{link} / {name, valid, already_member} / {pending} |
Files & storage
| cmd |
args |
data |
send_file |
conv, path |
File |
accept_file |
file_id, dir? |
– |
pause_file / cancel_file |
file_id |
– |
files |
– |
[File] |
storage_stats |
– |
usage stats (media_bytes, db_bytes, by_conv, policy, …) |
storage_policy / set_storage_policy |
– / max_cache?, media_days?, auto_direct?, auto_group?, community_days? |
policy |
clear_media |
conv?: "*" |
result |
maintain |
– |
runs storage cleanup |
Settings
| cmd |
args |
data |
settings_get |
– |
{read_receipts, typing, p2p_only, downloads_dir, …} |
settings_set |
read_receipts?, typing?, p2p_only?, downloads_dir? |
– (p2p_only = Pure P2P mode) |
Calls
| cmd |
args |
data |
call_start |
conv, video |
{call_id} |
call_answer |
call_id, accept |
– |
call_end |
call_id |
– |
call_set |
call_id, mic?, cam?, screen? |
– (screen = screen sharing) |
voice_join / voice_leave |
conv (voice channel) |
{call_id} / – |
Remote help
| cmd |
args |
data |
remote_status |
– |
{my_short_id, password, unattended, allow_control, allow_incoming, diagnostics} |
remote_set |
password?, unattended?, allow_control?, allow_incoming? |
status |
remote_new_password |
– |
{password} |
remote_trust |
peer, trusted?: true |
– |
remote_recent / remote_recent_remove |
– / peer? |
recent devices / – |
remote_connect |
target (short id or peer), password, view_only |
{session} |
remote_respond |
session, accept, control |
– |
remote_input |
session, ev (see Input events) |
– |
remote_quality |
session, preset: "smooth"|"balanced"|"sharp"|"saver" |
{max_kbps} |
remote_display |
session, index |
– |
remote_clipboard |
session, text |
– |
remote_power / remote_allow_power |
session, action: "lock"|"restart"|"logout" / allow |
– |
remote_end |
session |
– |
diagnostics |
– |
{platform, checks:[{id, ok, title, detail, fix_steps[]}]} (screen-capture / input permissions) |
{"t":"move","x":0.5,"y":0.25}
{"t":"button","b":"left","down":true}
{"t":"wheel","dx":0,"dy":-1}
{"t":"key","code":"KeyA","down":true}
{"t":"text","s":"Xin chào"}
x/y are normalised 0..1, b is left|right|middle, code uses W3C KeyboardEvent.code names, text sends unicode (IME).
Events (pushed)
| event |
payload |
ready |
{peer, short_id} |
presence |
{peer, online} |
contact |
Contact (added/updated) |
friend_request |
{peer, name, note} |
message |
Message (new, incoming or outgoing echo) |
message_update |
{conv, id, status?, body?, edited?, deleted?, reactions?, pinned?} |
mention |
{conv, id, author} |
callback |
{conv, msg_conv, msg_id, from, data}: a button on your message was pressed (bots) |
bot_user |
{peer, name}: a new user added your bot |
conversation / conversation_removed |
Conversation / {conv} |
typing |
{conv, peer, on} |
group |
group_info payload |
story / story_view |
{id, author} / {id, viewer} |
file |
File (progress / state changes) |
call |
{call_id, conv, peer, state: "ringing"|"incoming"|"active"|"ended", video, participants[]} |
video_stream |
{call_id, peer, stream} |
remote_request |
{session, peer, name, view_only} |
remote_state |
{session, peer, state: "connecting"|"active"|"ended"|"denied", width, height, role: "viewer"|"host", stream} |
clipboard |
{peer, text} |
storage_low |
{message} |
error |
{message} |
Data shapes
Contact {peer, short_id, name, alias, username, handle, status(0 none,1 sent,2 received,3 friend,4 blocked),
added, last_seen, verified, bio, presence, has_avatar, bot, online}
Conversation {id, kind(0 direct,1 group), title, last_ts, unread, muted, pinned, last_text}
Message {id, conv, author, hlc, ts, kind("text"|"file"|"sticker"|"voice"|"call"|"system"|"poll"|…), body,
status(0 pending,1 sent,2 delivered,3 read,9 failed), edited, deleted, reply_to,
reactions:{emoji:[peer]}, expires, pinned, meta:{mentions, mention_all, forwarded_from, buttons}, mine}
Button {text, data, url}
File {id, conv, peer, name, size, hash, path, direction(0 out,1 in),
state(0 offered,1 transferring,2 done,3 failed,4 cancelled,5 paused), progress(bytes), created, speed_bps?}