How to Integrate Karakeep with Other Services: A Complete tRPC and Webhooks Guide
Karakeep exposes a fully-typed tRPC API and a webhooks system that allows external services to both query data and receive real-time notifications about bookmark events.
Integrating with karakeep-app/karakeep enables you to build automated workflows, import legacy data, or sync bookmarks with third-party platforms. The repository provides two primary integration paths: a tRPC API for server-to-server calls and a webhooks infrastructure for event-driven architectures.
Authenticate with the Karakeep tRPC API
All API interactions require authentication via bearer tokens. Karakeep manages these through the apiKeys router, which implements secure key generation and revocation.
Generate API Keys
According to the source code in packages/trpc/routers/apiKeys.ts, you can programmatically create keys using the apiKeys.create mutation. This returns a secret key that must be attached to all subsequent requests.
import { createTRPCClient, httpBatchLink } from '@trpc/client';
// Initialize client (replace with your Karakeep deployment URL)
const client = createTRPCClient({
links: [httpBatchLink({ url: 'https://karakeep.example.com/trpc' })],
});
// Create API key (requires admin session or existing authentication)
const { key } = await client.apiKeys.create.mutate({
name: 'my-integration-key',
});
The apiKeys router handles the cryptographic storage of secrets in the database, ensuring the raw key is never exposed after creation.
Configure the tRPC Client with Authentication
For subsequent calls, attach the API key as a bearer token in the HTTP headers. The reference implementation in tools/seed-snapshot/src/index.ts demonstrates this pattern:
const client = createTRPCClient({
links: [
httpBatchLink({
url: 'https://karakeep.example.com/trpc',
fetch: (input, init) => {
init!.headers = {
...init!.headers,
Authorization: `Bearer ${process.env.KARAKEEP_API_KEY}`,
};
return fetch(input, init);
},
}),
],
});
Call the Karakeep API to Manage Bookmarks
Once authenticated, you can invoke type-safe procedures to manipulate bookmarks, lists, and tags. The tRPC implementation provides IDE autocomplete for all available methods.
Basic CRUD Operations
The following examples mirror the implementation in tools/seed-snapshot/src/index.ts:
// Create a bookmark with metadata
const bookmark = await client.bookmarks.createBookmark.mutate({
url: 'https://example.com/article',
title: 'Interesting article',
tags: ['tech', 'ai'],
});
// Query existing tags
const tags = await client.tags.list.query();
// Create a curated list
const list = await client.lists.create.mutate({
name: 'Reading Queue',
});
The seed-snapshot tool demonstrates advanced patterns including waiting for background crawlers to complete metadata extraction using await waitForCrawls(client, bookmarks).
Set Up Webhooks for Real-Time Event Notifications
Karakeep's webhook system, implemented in packages/trpc/routers/webhooks.ts, pushes event notifications to your infrastructure when data changes occur.
Register a Webhook Endpoint
Create a webhook subscription by calling the webhooks.create mutation. The payload must specify the target URL and the event types you wish to monitor.
await client.webhooks.create.mutate({
url: 'https://myservice.example.com/karakeep/webhook',
events: ['bookmark.created', 'bookmark.updated'],
});
The router implementation in packages/trpc/routers/webhooks.ts uses a toPublicWebhook helper (lines 16-22) to strip sensitive tokens from the response, exposing only a hasToken boolean flag. The actual token storage and validation logic resides in packages/trpc/models/webhooks.service.ts.
Handle Incoming Webhook Payloads
Your endpoint must accept HTTPS POST requests and respond with HTTP 200 within a few seconds. The JSON body follows the zWebhookSchema defined in packages/shared/types/webhooks.ts.
import express from 'express';
const app = express();
app.use(express.json());
app.post('/karakeep/webhook', (req, res) => {
const { event, data } = req.body;
console.log(`Received ${event}:`, data);
// Process event (e.g., sync to database, trigger notifications)
res.sendStatus(200);
});
Failed deliveries are automatically retried by the background worker defined in apps/workers/workers/webhookWorker.ts, which implements exponential backoff for resilient event delivery.
Integration Examples
Sync New Bookmarks to Slack
This Node.js example combines webhook reception with external API calls to forward bookmark notifications to a Slack channel:
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import fetch from 'node-fetch';
const KARAKEEP_URL = 'https://karakeep.example.com/trpc';
const SLACK_WEBHOOK = 'https://hooks.slack.com/services/XXX/YYY/ZZZ';
const client = createTRPCClient({
links: [
httpBatchLink({
url: KARAKEEP_URL,
async fetch(input, init) {
init!.headers = {
...init!.headers,
Authorization: `Bearer ${process.env.KARAKEEP_API_KEY}`,
};
return fetch(input, init);
},
}),
],
});
// Register webhook endpoint (run once during setup)
await client.webhooks.create.mutate({
url: 'https://my-server.example.com/karakeep/webhook',
events: ['bookmark.created'],
});
// Express handler to forward events
import express from 'express';
const app = express();
app.use(express.json());
app.post('/karakeep/webhook', async (req, res) => {
const { event, data } = req.body;
if (event === 'bookmark.created') {
await fetch(SLACK_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `📚 New bookmark: ${data.title}\n${data.url}`,
}),
});
}
res.sendStatus(200);
});
app.listen(3000);
Batch Import from CSV
Use the tRPC client to migrate existing bookmark archives into Karakeep:
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import fs from 'fs';
const client = createTRPCClient({
links: [httpBatchLink({ url: 'https://karakeep.example.com/trpc' })],
});
async function importCsv(filePath: string) {
const rows = fs.readFileSync(filePath, 'utf8').split('\n');
for (const row of rows) {
const [url, title, tags] = row.split(',');
await client.bookmarks.createBookmark.mutate({
url,
title,
tags: tags?.split('|') ?? [],
});
}
}
await importCsv('./my-links.csv');
Secure Webhook Verification
While Karakeep does not sign payloads, you can store a custom token during webhook creation and validate it on receipt:
app.post('/karakeep/webhook', async (req, res) => {
const { token, ...payload } = req.body;
if (token && token !== process.env.KARAKEEP_WEBHOOK_TOKEN) {
return res.sendStatus(403);
}
console.log('Validated payload:', payload);
res.sendStatus(200);
});
Summary
- Authenticate using the
apiKeys.createmutation inpackages/trpc/routers/apiKeys.tsto generate bearer tokens for API access. - Query data through the type-safe tRPC client by calling procedures like
bookmarks.createBookmarkortags.list, as demonstrated intools/seed-snapshot/src/index.ts. - Receive push notifications by registering webhooks via
webhooks.createinpackages/trpc/routers/webhooks.ts, with automatic retry logic handled byapps/workers/workers/webhookWorker.ts. - Handle security by storing verification tokens when creating webhooks (managed in
packages/trpc/models/webhooks.service.ts) and validating them in your endpoint handlers.
Frequently Asked Questions
Does Karakeep provide a REST API or only tRPC?
Karakeep exposes a tRPC API rather than traditional REST endpoints. You interact with it using the @trpc/client library with an HTTP-batch link, which provides end-to-end type safety and autocomplete without requiring separate OpenAPI specifications.
How do I authenticate API requests to Karakeep?
Generate an API key using the apiKeys.create mutation defined in packages/trpc/routers/apiKeys.ts, then attach it as a Bearer token in the Authorization header of every request. The key is generated once and must be stored securely by your application.
What events can trigger Karakeep webhooks?
The webhook system supports events such as bookmark.created, bookmark.updated, and list modification events. When registering a webhook in packages/trpc/routers/webhooks.ts, you specify an array of event strings to filter which notifications you receive.
How does Karakeep handle failed webhook deliveries?
The apps/workers/workers/webhookWorker.ts background service implements an exponential backoff retry mechanism. If your endpoint returns a non-200 status code or fails to respond, Karakeep automatically retries delivery multiple times before marking the webhook as failed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →