Menu
holagram

The server

createHolagram(): the route handler, its options, its endpoints and what it stores in Redis.

app/api/holagram/[action]/route.ts
ts
import { createHolagram } from '@angelitolm/holagram/server'
 
export const { GET, POST } = createHolagram({
  ownerName: 'Ana',
})

createHolagram() returns a GET and a POST handler of type (req: Request) => Promise<Response>. They read the action from the last segment of the URL, which is why the Next route is a dynamic segment [action].

Options

OptionDefault
ownerName'us'Who the visitor talks to, in the verification email.
emailFromHOLAGRAM_EMAIL_FROMSender of the verification email.
siteUrlSITE_URL, then Vercel's production URL, then localhostOrigin of the verification link. See Email verification.
emailshort English email({ name, link, ownerName }) => ({ subject, text, html }).
prefix'holagram'Prefix for every Redis key, to share a database with other apps.

Environment variables

Variable
TELEGRAM_BOT_TOKENFrom @BotFather.
TELEGRAM_CHAT_IDYour private chat with the bot: npx holagram chat-id.
TELEGRAM_WEBHOOK_SECRETAny string of letters, numbers, _ and -: npx holagram secret.
KV_REST_API_URL, KV_REST_API_TOKENUpstash Redis REST. UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN also work.
RESEND_API_KEYFrom Resend.
HOLAGRAM_EMAIL_FROMUnless you pass emailFrom.
SITE_URLOptional.

If any is missing, the endpoints answer 503 (the webhook, 401) and the widget says it's offline. POST /start also lists the names of the missing variables (never their values), so you can check from the browser's network tab:

json
{ "error": "not_configured", "missing": ["RESEND_API_KEY"] }

Endpoints

Endpoint
POST /start{ name, email, topic, path }: sends the verification email.
POST /verify{ token }: consumes the link, opens the conversation and notifies you on Telegram. Returns { sid, name, email, topic }.
GET /messages?sid=&after=The conversation's messages from index after.
POST /messages{ sid, text }: a visitor message, forwarded to Telegram.
POST /webhookTelegram's webhook. Requires the secret header.

Errors are { error } with 400 invalid, 404 not_found, 410 expired, 429 rate_limited, 502 failed or 503 not_configured.

What it stores

Everything in Upstash Redis, under prefix:

KeyValueExpires
holagram:verify:<sha256(token)>name, email, topic30 min, or when used
holagram:<sid>the conversation: name, email, topic, created at30 days after the last message
holagram:<sid>:msgslist of { from, text, at }30 days after the last message
holagram:tg:<message_id>the sid that Telegram message belongs to30 days
holagram:rl:<key>rate limit counters1 hour

Other runtimes

The handlers only use fetch, Request, Response and Web Crypto. In Hono, for example:

ts
import { Hono } from 'hono'
import { createHolagram } from '@angelitolm/holagram/server'
 
const holagram = createHolagram({ ownerName: 'Ana' })
const app = new Hono()
app.all('/api/holagram/:action', (c) => holagram.POST(c.req.raw)) // GET and POST are the same handler