QivunaDevelopers

Getting started

Create a bot in the Qivuna app with @qivbot, reveal its token, and run an echo bot. About ten minutes.

Before you start#

To create bots, your Qivuna account needs:

Each account may own 2 bots; 10 once it has been a bot developer for 30 days without any content strike.

1. Create the bot with @qivbot#

  1. Open @qivbot in the Qivuna app (search for qivbot, or open https://qivuna.com/b/qivbot) and press Start. It answers in Hebrew if your app is in Hebrew, otherwise in English.
  2. Tap New bot (or send /newbot). If something from the list above is missing, QivBot says what, with a button to fix it.
  3. Read the Developer Terms and tap I agree.
  4. Send the bot's name: 1–64 characters. Check marks, “official”, “verified” and the name Qivuna are not allowed in names of bots without the blue check.
  5. Send its username (handle): 7–32 characters, starting with a letter, letters, digits and _, ending in bot — for example weather_bot. Names that look like Qivuna's own (including look-alike letters) are refused.
  6. Choose Encrypted (recommended) or Not encrypted. An unencrypted bot is labelled “not end-to-end encrypted” for everyone and cannot work in encrypted groups. You can switch a bot to encrypted later, never back. (This step appears once encrypted bots are switched on for the platform; until then bots are created unencrypted.)

QivBot answers “@weather_bot is ready! It's in development mode” with buttons for the token, settings, commands and publishing.

2. Reveal the token#

Tap Reveal token. The app asks for your authenticator code or 2-step password and shows the token once, on a secure screen:

7123456789:qvb1_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-AbCdE

The part before the colon is the bot number; the rest is a 256-bit secret. Treat it like a password:

Keep it in an environment variable, not in code:

export QIVUNA_BOT_TOKEN='7123456789:qvb1_…'

3. Run an echo bot#

Packages not published yet The SDKs are finished and tested, but not yet on npm or PyPI. The install commands below will work once they are published; this page will say so.

Node.js (20.11 or later)#

npm install @qivuna/bot-sdk

echo-bot.mjs

import { Bot, FileStateStore } from "@qivuna/bot-sdk";

const bot = new Bot({
  token: process.env.QIVUNA_BOT_TOKEN,
  store: new FileStateStore("./bot-state"),   // replay memory, keys, pins: keep and back up
});

bot.onMessage(async (ctx) => {
  if (ctx.text) await ctx.reply(ctx.text, { quote: true });
});

bot.onCallbackQuery(async (ctx) => {
  await ctx.answer(`You pressed ${ctx.data}`);
});

process.once("SIGINT", () => bot.stop());
process.once("SIGTERM", () => bot.stop());
await bot.startPolling();
await bot.store.close();
node echo-bot.mjs

Python (3.9 or later)#

pip install qivuna-bot-sdk

echo_bot.py

import os, signal
from qivuna_bot import Bot, FileStateStore

bot = Bot(os.environ["QIVUNA_BOT_TOKEN"], FileStateStore("./bot-state"))

@bot.on_message
def echo(ctx):
    if ctx.text:
        ctx.reply(ctx.text, quote=True)

@bot.on_callback_query
def pressed(ctx):
    ctx.answer("You pressed %s" % ctx.data)

signal.signal(signal.SIGINT, lambda *a: bot.stop())
bot.run_polling()
bot.store.close()
python echo_bot.py

Without an SDK#

The API is plain JSON over HTTPS. A minimal loop: long-poll, answer, acknowledge.

OFFSET=0
while true; do
  RESP=$(curl -s "https://bots.qivuna.com/v1/getUpdates" \
    -H "Authorization: Bot $QIVUNA_BOT_TOKEN" -H "Content-Type: application/json" \
    -d "{\"offset\": $OFFSET, \"timeout\": 25}")
  if [ "$(echo "$RESP" | jq .ok)" != "true" ]; then echo "$RESP" >&2; sleep 5; continue; fi
  echo "$RESP" | jq -c '.result[]' | while read -r U; do
    CHAT=$(echo "$U" | jq '.message.chat.id // empty')
    TEXT=$(echo "$U" | jq '.message.text // empty')
    [ -n "$CHAT" ] && [ -n "$TEXT" ] && curl -s "https://bots.qivuna.com/v1/sendMessage" \
      -H "Authorization: Bot $QIVUNA_BOT_TOKEN" -H "Content-Type: application/json" \
      -d "{\"chat_id\": $CHAT, \"text\": $TEXT}" > /dev/null
  done
  LAST=$(echo "$RESP" | jq '.result[-1].update_id // empty')
  [ -n "$LAST" ] && OFFSET=$((LAST + 1))
done

A real bot should also handle errors and retry_after; see Errors.

4. Try it#

  1. Open https://qivuna.com/b/<your bot's username> on your phone (or find the bot from QivBot → My bots) and press Start. Your bot receives a bot_started update.
  2. Send it a message; it echoes it back.
  3. Add it to a small group of your own: group info → Bots → Add → search its username → choose its rights. In development mode the group must be yours and have at most 10 members (see Development mode).
  4. In the group, send /start@your_bot or mention it: with privacy mode on, that is what it sees.

If you chose Encrypted#

Not open yet Encrypted bots are not available at launch: QivBot creates every new bot as a plain bot, and the chat shows that it is not end-to-end encrypted. This section describes how they will work.

Nobody can start an encrypted bot until it has an active key: the app says “This bot isn't ready yet”. The key pair is made on your server, and you, the owner, confirm it on your phone:

await bot.init();
let status = await bot.syncKeys();
if (!status.canSeal) {
  await bot.generateKey();                        // private key stays in ./bot-state
  const { fingerprint } = await bot.proposeKey(); // setMyEncryptionKey
  console.log("security code:", fingerprint);
  while (!(status = await bot.syncKeys()).canSeal) await new Promise((r) => setTimeout(r, 10_000));
}
bot.onEncryptedMessage(async (req) => { await req.reply(`You said: ${req.text}`); });
await bot.startPolling();

Then in the app: QivBot → your bot → Encryption → Confirm new code. The confirmation screen computes the security code on your phone; check that it matches the one your server printed, tap It matches and confirm with your authenticator code or 2-step password. The full story, including what this protects, is in Encrypted bots.

Next steps#