Mint a bot in the app
Open Qwing → Settings → My Bots → Create. Set a name, username, description and emoji avatar. You receive a 24-word recovery phrase, shown once — that phrase is the bot's token.
Run the SDK on your server
Plug that token into the Rust SDK, define how the bot replies, and run it on your own machine. It publishes prekeys, holds the single-instance lock, and polls our relay for new messages.
Overview
What a Qwing bot is
A bot is a normal Qwing account you control from code. It has its own Ed25519 + ML-DSA-87 identity, its own X25519 + ML-KEM-1024 sessions, and it talks to users over the exact same end-to-end-encrypted channel that people use. There is no bot API that sees plaintext: the server stores and forwards ciphertext, and your bot process does the decryption locally with keys derived from its token.
Create
Mint the bot identity
Bots are created from inside Qwing, not on a website — the private key never leaves your device until you copy it. In Settings → My Bots, accept the Bot Developer Agreement, then set the profile (name, username, description, single-emoji avatar). Qwing shows the bot's 24-word recovery phrase exactly once. Store it somewhere safe.
Quickstart
Run the SDK
The SDK is a small self-hostable Rust crate. Give it the token, register your handlers, and call run() — it connects, publishes prekeys, and polls for messages until it is stopped.
use qwing_bot_sdk::BotClient;
#[tokio::main]
async fn main() -> qwing_bot_sdk::Result<()> {
let token = std::env::var("BOT_TOKEN").expect("set BOT_TOKEN");
let mut builder = BotClient::from_token(&token).with_data_dir("./qwing-echo-data");
if let Ok(url) = std::env::var("BOT_BACKEND") { builder = builder.with_backend(&url); }
if let Ok(proxy) = std::env::var("BOT_PROXY") { builder = builder.with_proxy(&proxy); }
let mut bot = builder.connect().await?; // derives identity, publishes prekeys
bot.on_message(|msg, ctx| async move {
match msg.text.as_str() {
"/start" => ctx.reply("Hi! Send me anything and I'll echo it.").await,
other => ctx.reply(&format!("echo: {other}")).await,
}
});
bot.run().await // polls until stopped
}
Set the token and the API endpoint in the environment:
# liboqs must be on the library path at build and run time
export BOT_TOKEN="word1 word2 … word24"
export BOT_BACKEND="https://api.quantumwing.io/api/v1"
cargo run --release
Handlers
Commands & replies
There is no fixed command set — you decide what the bot understands inside on_message by matching on msg.text. Commands are just messages that start with / by convention. The context object gives you the reply primitives:
- ctx.reply(text)Send a plain text reply to the user who messaged the bot.
- ctx.reply_with_buttons(text, rows)Reply with tappable inline buttons; each carries a callback payload.
- bot.on_button(cb)Handle a button tap —
cb.datais the payload you attached. - msg.textThe decrypted inbound text. Match it to route
/start,/help, or anything you define.
bot.on_message(|msg, ctx| async move {
match msg.text.as_str() {
"/start" => ctx.reply("Welcome. Try /menu").await,
"/menu" => ctx.reply_with_buttons("Pick one:",
&[&[("Weather", "pick:weather"), ("News", "pick:news")]]).await,
_ => ctx.reply("Unknown command — try /start").await,
}
});
bot.on_button(|cb, ctx| async move {
ctx.reply(&format!("You picked {}", cb.data)).await
});
Rules
One token, one instance
Run exactly one process per token. The bot holds a single-instance lock; two processes on the same token would fork the ratchet and permanently break decryption for the people it talks to. If you redeploy, stop the old instance first, and keep the bot's data directory with it — that is where its session state lives.
Privacy
What the server sees
The same as for any Qwing chat: no message content. Messages between a user and your bot are end-to-end encrypted with hybrid (classical + post-quantum) cryptography; the server relays ciphertext and cannot read it. Your bot is the other end of that encryption, so it naturally sees the messages sent to it — and nothing else. It has no window into any other conversation. As with any chat, the server can work out who is talking to your bot (see the Privacy Policy).
Ready to build? Mint a bot in the app and point the SDK at its token.