Ask Channel AI

Prompt API for bots & scripts

Send a prompt, get a cited answer. Use your MCP token (askchannelai_...) as a Bearer token and POST to one REST endpoint. You always get the Markdown answer + structured citations; add a platform (telegram, discord, whatsapp, slack) to also get a ready-to-send rendered field in that app's own format — Telegram HTML, Slack/WhatsApp mrkdwn, or Discord embeds. The server does the formatting. Create a token on the Tokens page; the examples below are prefilled with your latest token.

Try it

provider

platform

Create a token on the Tokens page to run.

Code examples

TypeScript SDK — @askchannelai/sdk (recommended)

// npm i @askchannelai/sdk
import { createAskChannelClient } from '@askchannelai/sdk';

const ac = createAskChannelClient({ token: 'askchannelai_your_full_secret_here' }); // your askchannelai_… token

// Ask a cited question. Pass a platform to get a ready-to-send "rendered" field.
const res = await ac.ask({
  channel: '@apple',                 // @handle (with/without @) or a UC… id
  question: 'What is the latest MacBook Pro model?',
  platform: 'telegram',              // telegram | discord | whatsapp | slack
});
console.log(res.answer);             // clean prose (always)
console.log(res.citations);          // [{ videoTitle, startTime, url, ... }] (always)
console.log(res.rendered);           // ready to send — HTML for telegram, etc.

// Search the channels you can ask — e.g. to build a picker / dropdown:
const channels = await ac.searchChannels('apple'); // [{ id, title, handle, ... }]

The official, dependency-free SDK wraps this endpoint and channel search. For Telegram, @askchannelai/telegram layers on the inline channel-picker, stateless reply-recovery, and ready-to-send HTML — see the inline-picker snippet below. npm · source · full Telegram bot example. Prefer raw HTTP? The same calls below work in any language.

cURL (prompt a channel)

curl -X POST 'https://askchannel.ai/api/v1/channel/qa/@apple' \
  -H 'Authorization: Bearer askchannelai_your_full_secret_here' \
  -H 'Content-Type: application/json' \
  -d '{
    "question": "What is the latest Macbook Pro model and what upgrades does it have?",
    "provider": "gemini",
    "platform": "telegram"
  }' | jq

Node / fetch (minimal)

// One call. Add "platform" to get a ready-to-send "rendered" field.
const res = await fetch('https://askchannel.ai/api/v1/channel/qa/@apple', {
  method: 'POST',
  headers: { Authorization: 'Bearer askchannelai_your_full_secret_here', 'Content-Type': 'application/json' },
  body: JSON.stringify({ question: 'What is the latest Macbook Pro model?', provider: 'gemini', platform: 'telegram' }),
});
const data = await res.json();
console.log(data.answer);     // raw Markdown (always returned)
console.log(data.citations);  // structured [{ videoTitle, startTime, url, ... }] (always)
console.log(data.rendered);   // formatted for "platform" — HTML here; Discord returns { content, embeds }

Pass platform in the body and the server returns a ready-to-send rendered field — no formatting code needed. You still set the platform's own send flag: Telegram parse_mode:'HTML', Slack mrkdwn:true (default); WhatsApp reads the markup directly; Discord's rendered is a { content, embeds } object you reply with as-is.

Telegram bot

// npm i node-telegram-bot-api
import TelegramBot from 'node-telegram-bot-api';

const bot = new TelegramBot(process.env.TELEGRAM_TOKEN, { polling: true });
const PROMPT_URL = 'https://askchannel.ai/api/v1/channel/qa/@apple';

bot.on('message', async (msg) => {
  const question = (msg.text || '').trim();
  if (!question) return;
  const res = await fetch(PROMPT_URL, {
    method: 'POST',
    headers: { Authorization: 'Bearer askchannelai_your_full_secret_here', 'Content-Type': 'application/json' },
    body: JSON.stringify({ question, provider: 'gemini', platform: 'telegram' }),
  });
  const data = await res.json();
  if (data.error) return bot.sendMessage(msg.chat.id, 'Prompt failed: ' + data.error);
  // data.rendered is ready-to-send HTML — just set parse_mode.
  await bot.sendMessage(msg.chat.id, data.rendered, { parse_mode: 'HTML', disable_web_page_preview: true });
});

Telegram bot — inline channel-picker (@askchannelai/telegram)

// npm i @askchannelai/sdk @askchannelai/telegram grammy
import { Bot } from 'grammy';
import { createAskChannelClient } from '@askchannelai/sdk';
import { buildInlineResults, channelRefFromPickReply, qaErrorMessage } from '@askchannelai/telegram';

const ac = createAskChannelClient({ token: 'askchannelai_your_full_secret_here' });
const bot = new Bot(process.env.TELEGRAM_TOKEN);

// @yourbot <name> → searchable dropdown of indexed channels
bot.on('inline_query', async (ctx) => {
  const channels = await ac.searchChannels(ctx.inlineQuery.query);
  await ctx.answerInlineQuery(buildInlineResults(channels));
});

// A reply to a picked channel = the question for it (stateless — the ref rides in the prompt)
bot.on('message:text', async (ctx) => {
  const r = ctx.message.reply_to_message;
  const ref = r && channelRefFromPickReply({ via_bot: r.via_bot, entities: r.entities }, ctx.me.username);
  if (!ref) return;
  try {
    const res = await ac.ask({ channel: ref, question: ctx.message.text, platform: 'telegram' });
    await ctx.reply(res.rendered, { parse_mode: 'HTML' });
  } catch (e) {
    await ctx.reply(qaErrorMessage(ref, e), { parse_mode: 'HTML' });
  }
});

bot.start();

A searchable channel dropdown in a few lines: buildInlineResults renders the picker, channelRefFromPickReply recovers the picked channel from the user's reply, and qaErrorMessage formats failures. Full runnable bot in the example.

Discord bot

// npm i discord.js
import { Client, GatewayIntentBits } from 'discord.js';

const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
const PROMPT_URL = 'https://askchannel.ai/api/v1/channel/qa/@apple';

client.on('messageCreate', async (message) => {
  if (message.author.bot || !message.mentions.has(client.user)) return; // only when @bot
  const question = message.content.replace(/<@!?\d+>/g, '').trim();
  if (!question) return;
  const res = await fetch(PROMPT_URL, {
    method: 'POST',
    headers: { Authorization: 'Bearer askchannelai_your_full_secret_here', 'Content-Type': 'application/json' },
    body: JSON.stringify({ question, provider: 'gemini', platform: 'discord' }),
  });
  const data = await res.json();
  // For Discord, data.rendered is { content, embeds } — reply with it directly.
  await message.reply(data.rendered);
});

client.login(process.env.DISCORD_TOKEN);

WhatsApp (Twilio)

// Twilio WhatsApp webhook (Express)
import express from 'express';
import twilio from 'twilio';

const app = express();
app.use(express.urlencoded({ extended: false }));
const PROMPT_URL = 'https://askchannel.ai/api/v1/channel/qa/@apple';

app.post('/whatsapp', async (req, res) => {
  const question = (req.body.Body || '').trim();
  const r = await fetch(PROMPT_URL, {
    method: 'POST',
    headers: { Authorization: 'Bearer askchannelai_your_full_secret_here', 'Content-Type': 'application/json' },
    body: JSON.stringify({ question, provider: 'gemini', platform: 'whatsapp' }),
  });
  const data = await r.json();
  const twiml = new twilio.twiml.MessagingResponse();
  twiml.message(data.rendered); // WhatsApp reads the markup directly
  res.type('text/xml').send(twiml.toString());
});

app.listen(3000);

Slack (Bolt)

// npm i @slack/bolt
import pkg from '@slack/bolt';
const { App } = pkg;

const app = new App({ token: process.env.SLACK_BOT_TOKEN, signingSecret: process.env.SLACK_SIGNING_SECRET });
const PROMPT_URL = 'https://askchannel.ai/api/v1/channel/qa/@apple';

app.event('app_mention', async ({ event, say }) => {
  const question = event.text.replace(/<@[^>]+>/g, '').trim();
  const r = await fetch(PROMPT_URL, {
    method: 'POST',
    headers: { Authorization: 'Bearer askchannelai_your_full_secret_here', 'Content-Type': 'application/json' },
    body: JSON.stringify({ question, provider: 'gemini', platform: 'slack' }),
  });
  const data = await r.json();
  await say({ text: data.rendered, mrkdwn: true }); // mrkdwn is Slack's default
});

(async () => { await app.start(); })();

Endpoint

POST /api/v1/channel/qa/:channelId

Body: { question: string, provider?: 'gemini' | 'claude', platform?: 'telegram' | 'discord' | 'whatsapp' | 'slack' }
Response: { answer, citations: [{ videoId, videoTitle, startTime, url, ... }], provider, rendered? }
Citations include YouTube deep links with timestamps. The rendered field is present only when platform is set (HTML for telegram, mrkdwn for slack/whatsapp, a { content, embeds } object for discord).

Rate limits and daily question quotas apply (tied to the token owner's account). For multi-turn bot conversations, create a conversation first via the conversations API and use /search, or just prompt the channel fresh each time.