Recipe

Telegram cron bot

Two cron jobs: one polls Telegram and captures messages into quik.md; the other posts the morning open-todos digest back to your private chat.

Setup

Create a Telegram bot via @BotFather, grab the token. Mint a quik.md API key in Settings → Developer. Drop both into a Cloudflare Worker / Vercel cron / your own box.

Capture loop (every minute)

// poll-telegram.mjs — run on a 1-minute cron
const TG = process.env.TELEGRAM_BOT_TOKEN
const QK = process.env.QUIK_KEY
let offset = Number(globalThis.OFFSET ?? 0)

const updates = await fetch(
  `https://api.telegram.org/bot${TG}/getUpdates?offset=${offset}&timeout=0`,
).then((r) => r.json())

for (const u of updates.result ?? []) {
  offset = u.update_id + 1
  const text = u.message?.text
  if (!text) continue

  await fetch("https://quik.md/api/v1/capture", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${QK}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ text, organize: true, source: "api" }),
  })
}
globalThis.OFFSET = offset

Voice messages: download the file via getFile, then forward the bytes to /api/v1/voice/transcribe; pipe the resulting text back into /api/v1/capture.

Morning digest (07:30 daily)

// morning-digest.mjs — daily cron
const QK = process.env.QUIK_KEY
const TG = process.env.TELEGRAM_BOT_TOKEN
const CHAT = process.env.TELEGRAM_CHAT_ID

const today = new Date()
today.setHours(23, 59, 59, 999)

const r = await fetch(
  `https://quik.md/api/v1/items/search?status=todo&due_before=${today.toISOString()}&limit=20`,
  { headers: { Authorization: `Bearer ${QK}` } },
).then((r) => r.json())

const lines = r.items.map((i) => `• ${i.title || i.content_md.slice(0, 80)}`)
const text = lines.length
  ? `Today's plan:\n${lines.join("\n")}`
  : "Inbox zero. Take it easy."

await fetch(`https://api.telegram.org/bot${TG}/sendMessage`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ chat_id: CHAT, text }),
})

Toggle from Telegram

Bot replies to the digest message can call POST /api/v1/items/:id/toggle by parsing the item id from the digest line. Or use search → bulk-toggle to mark multiple done from a single message.