Skip to content

A Fedify project · built by the Fedify team

Fediverse bots,
as standalone servers.

BotKit is a TypeScript framework for standalone ActivityPub bots. No Mastodon account and no 500‑character limit: your bot runs as a complete fediverse server, and it fits in a single file.

deno add jsr:@fedify/botkit
The BotKit dinosaur, held on its model-kit runner

A complete bot in one file

Create the bot, answer events, and publish. This is the whole thing, with no accounts to register and no platform to ask permission from.

weatherbot.ts
import { 
createBot
,
MemoryKvStore
,
text
} from "@fedify/botkit";
const
bot
=
createBot
<void>({
username
: "weatherbot",
name
: "Seoul Weather Bot",
summary
:
text
`I post daily weather updates for Seoul!`,
kv
: new
MemoryKvStore
(),
}); // Reply when someone mentions the bot
bot
.
onMention
= async (
session
,
message
) => {
await
message
.
reply
(
text
`It's 18°C with clear skies in Seoul.`);
}; // Publish on a schedule
setInterval
(async () => {
const
session
=
bot
.
getSession
("https://weather.example.com");
await
session
.
publish
(
text
`Good morning! Today: 22°C, clear skies ☀️`);
}, 1000 * 60 * 60 * 24);
  • createBot() gives the bot an identity and storage. That is the entire setup.
  • onMention and its siblings are just async functions you assign.
  • text`…` builds safe, formatted content: mentions, hashtags, and links included.

Standalone

Its own server, not an account

A BotKit bot is not a script driving an account on someone else's Mastodon or Misskey server. It is an ActivityPub actor served by your app, on your domain, backed by your storage, your queue, and your code. It still federates with Mastodon, Misskey, and the rest of the fediverse.

More on standalone bots →
Account automation
  • Mastodon or Misskey account
  • Access token
  • Platform database
  • Platform limits
  • One account at a time
BotKit server
  • ActivityPub actor on your domain
  • Your repository
  • Your queue
  • Your message rules
  • One app, many bots

Type safety

The compiler knows your bot

Give BotKit your app's context type once, and it follows your bot through sessions, event handlers, message builders, and helper functions. TypeScript catches mismatched context data and invalid message options before a fediverse server ever sees the activity.

More on BotKit's types →
support-bot.ts
bot
.
onMention
= async (
session
,
message
) => {
const
tenantName
=
session
.
context
.
data
.
tenantName
;
session
.
await
message
.
reply
(
text
`Welcome to ${
tenantName
}!`);
await
session
.
publish
("Welcome!");
Argument of type 'string' is not assignable to parameter of type 'Text<"block", AppContext>'.
};
Context travels with the sessionHandlers receive typed messagesPublish options stay checked

Messages

Rich messages, safely composed

Write posts with the text`…` template. It escapes HTML for you and understands mentions, hashtags, links, and custom emoji. Attach images, open polls, collect emoji reactions, and allow quote posts with consent (FEP‑044f). Choose each post's visibility, then edit or delete it later.

Read about messages →
post.ts
await 
session
.
publish
(
text
`New chart is up! ${
hashtag
("BotKit")}`,
{
attachments
: [new
Image
({
url
,
mediaType
: "image/png" })],
visibility
: "public",
}, );

Events

Answer what happens

Assign an async function to respond to activity: mentions, replies, follows and unfollows, quotes, poll votes, and emoji reactions. Every handler receives a session, so it can publish, reply, or react in return.

See all events →
handlers.ts
bot
.
onFollow
= async (
session
,
follower
) => {
await
session
.
publish
(
text
`Thanks for the follow, ${
follower
}!`, {
visibility
: "direct",
}); };
bot
.
onReact
= async (
session
,
reaction
) => {
await
reaction
.
message
.
reply
(
text
`Glad you liked it!`);
};

Instance

Many bots, one server

Need more than one bot? createInstance() owns the shared infrastructure (the key‑value store, queue, repository, and HTTP handling), and each bot on it keeps its own actor, handle, and event handlers. Declare static bots up front, or resolve a whole family of bots from a database on demand.

Read about instances →
instance.ts
const 
instance
=
createInstance
<void>({
kv
: new
MemoryKvStore
(),
}); const
greetBot
=
instance
.
createBot
("greet", {
username
: "greetbot",
name
: "Greeting Bot",
}); const
echoBot
=
instance
.
createBot
("echo", {
username
: "echobot",
name
: "Echo Bot",
}); export default
instance
;

Web

A web presence, out of the box

BotKit serves each bot's own pages: a profile, individual posts, the follower list, hashtag pages, and an Atom feed. They render in the bot's accent color, adapt to light and dark, and you can restyle them with the color, theme, and css options. Nothing extra to deploy.

More on web pages →
Greeting Bot
@greetbot@example.com

I greet everyone who follows me. 👋

128 followers342 posts

Also in the box

Everything else that comes with the framework.

Type‑safe API

Written in TypeScript end to end: autocomplete, compile‑time checks, and typed builders for every message you send.

Follower policy

Approve every follower automatically, or review each follow request yourself before accepting.

Quote controls

Support Misskey-style quotes and Mastodon-style, consent-respecting quotes with FEP-044f policies.

Polls and votes

Publish single-choice or multiple-choice polls, then react when people vote on your bot's questions.

Emoji reactions

Send emoji reactions, receive reaction events, and handle undo events when someone takes a reaction back.

Deno, Node.js, and Workers

Runs on Deno, Node.js, and Cloudflare Workers with minimal dependencies. A whole bot fits in a single TypeScript file.


Built on Fedify

BotKit is the sister project of Fedify, built by the same team. Fedify does the hard part of federation: the ActivityPub protocol, compatibility with Mastodon, Misskey, and the rest of the fediverse, signed message delivery, and retries. BotKit adds the bot on top: events, sessions, messages, and storage.

Learn about Fedify →

Store it and ship it anywhere

Put storage behind BotKit's Repository interface. Your handlers keep the same shape when you move from memory to Redis, PostgreSQL, SQLite, Deno KV, or a hosted runtime.

bot.ts
const bot = createBot({
  username: "support",
  repository: new KvRepository(kv),
});

bot.onMention = async (session, message) => {
  await message.reply(text`Hello!`);
};

Only the repository line changes

The repository line owns persistence. The event handler below it does not know whether the bot is backed by a process, a database, or a serverless key-value store.

Stable:

Handlers, sessions, and message builders

Swappable:

Repository adapter and deploy target


Build your first bot

Install the package and follow the guide, and you'll have a bot on the fediverse in minutes.

deno add jsr:@fedify/botkit