> ## Documentation Index
> Fetch the complete documentation index at: https://docs.towns.com/llms.txt
> Use this file to discover all available pages before exploring further.

# External Integrations

Towns bots are built on [Hono](https://hono.dev). While `/webhook` is reserved for Towns events, you can add custom routes for external webhooks, APIs, and scheduled tasks.

## Using Bot Methods Anywhere

Bot methods like `sendMessage()` can be called directly on the bot instance, outside of event handlers. This enables integration with external services.

```ts theme={null}
import { Hono } from 'hono'
import { makeTownsBot } from '@towns-protocol/bot'

const bot = await makeTownsBot(privateData, jwtSecret)
const { jwtMiddleware, handler } = bot.start()

const app = new Hono()

// Towns webhook (required)
app.post('/webhook', jwtMiddleware, handler)

// Custom API endpoint
app.post('/api/send', async (c) => {
  const { channelId, message } = await c.req.json()
  await bot.sendMessage(channelId, message)
  return c.json({ ok: true })
})

export default app
```

## External Webhooks

Integrate with external services like GitHub, Linear, Stripe, or custom APIs.

```ts theme={null}
import { Hono } from 'hono'
import { makeTownsBot } from '@towns-protocol/bot'

const bot = await makeTownsBot(privateData, jwtSecret, { commands })

let notificationChannelId: string | null = null

// Configure channel for notifications
bot.onSlashCommand('setup', async (handler, event) => {
  notificationChannelId = event.channelId
  await handler.sendMessage(event.channelId, 'Notifications configured')
})

const { jwtMiddleware, handler } = bot.start()
const app = new Hono()

app.post('/webhook', jwtMiddleware, handler)

// External webhook endpoint
app.post('/external-webhook', async (c) => {
  const payload = await c.req.json()
  
  if (!notificationChannelId) {
    return c.json({ error: 'No channel configured' }, 400)
  }
  
  // Send events to Towns channel
  await bot.sendMessage(
    notificationChannelId,
    `Event received: ${payload.type}`
  )
  
  return c.json({ success: true })
})

export default app
```

You can use this pattern with any service that has webhook integration. Alchemy, GitHub, Linear, etc.

## Scheduled Tasks

Use intervals or cron jobs to send periodic updates.

```ts theme={null}
const bot = await makeTownsBot(privateData, jwtSecret)
const channels = new Set()

bot.onSlashCommand('alerts', async (handler, event) => {
  channels.add(event.channelId)
  await handler.sendMessage(event.channelId, 'Alerts enabled')
})

// Run every 5 minutes
setInterval(async () => {
  const data = await fetch('https://api.example.com/data').then(r => r.json())
  
  for (const channelId of channels) {
    await bot.sendMessage(channelId, `Update: ${data.value}`)
  }
}, 5 * 60 * 1000)
```

## Persistent Storage

<Warning>
  In-memory storage (Map, Set) only works on always-on servers. Use a database for production.
</Warning>

Popular options:

* **SQLite/Turso** - Simple, serverless-compatible
* **Redis** - Fast caching and rate limiting
* **PostgreSQL** - Full-featured relational database
* **Supabase** - Managed PostgreSQL with APIs

## Next Steps

* Explore [onchain integrations](/build/bots/onchain-integrations)
* Learn about [slash commands](/build/bots/slash-commands)
* Read about [event handlers](/build/bots/events)
