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"
}' | jqNode / 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.