Corsair
Concepts

Database Concepts

Supported databases, ORMs, and schema detection

No matter how many integrations you add, Corsair uses only four tables. This keeps your database clean and your queries fast.

Core Tables

corsair_providers

All integrations you have enabled.

-- Example row
id: "slack"
config: { ... }

corsair_connections

The tenant connections for each integration. If multi-tenancy is off, this is 1:1 with providers.

-- Example rows
id: 1, tenant_id: "tenant_abc", provider_id: "slack", config: { botToken: "..." }
id: 2, tenant_id: "tenant_xyz", provider_id: "slack", config: { botToken: "..." }

corsair_resources

Every piece of data from your integrations, cleaned up and constantly updated.

-- Example row
id: "uuid", tenant_id: "tenant_abc", resource: "slack", service: "messages"
resource_id: "msg_123", data: { text: "Hello!", channel: "C01234567" }

When you send a Slack message, Corsair stores it here. When a webhook arrives saying the message was updated, Corsair updates this row. Your data is never stale.

corsair_events

A log of all events for replay and auditing.

-- Example rows
id: 1, resource_id: "msg_123", event_type: "created", ...
id: 2, resource_id: "msg_123", event_type: "updated", ...

Want to see a message's history? Query this table to replay every change.

Querying Data

Use the Corsair ORM to query data with full type safety.

example.ts
// Find a specific resource
const message = await corsair.slack.db.messages.findByResourceId("msg_123");

// List all messages for this tenant
const messages = await corsair.slack.db.messages.list({ limit: 50 });

// Search by data fields (JSONB)
const results = await corsair.slack.db.messages.search({
    data: { channel: "C0123456789" },
    limit: 20,
});

// Search with contains (LIKE)
const partialMatch = await corsair.slack.db.channels.search({
    data: { name: { contains: "general" } },
});

Foreign Keys

You can create foreign keys from your own tables to corsair_resources. Since Corsair keeps this data fresh through API calls and webhooks, your references always point to up-to-date data.

-- Your table
CREATE TABLE my_tickets (
    id UUID PRIMARY KEY,
    slack_message_id UUID REFERENCES corsair_resources(id),
    ...
);

Database Configuration

Corsair connects directly to PostgreSQL. Pass either a pg Pool or a Kysely instance.

corsair.ts
import { Pool } from "pg";

export const corsair = createCorsair({
    database: new Pool({ connectionString: process.env.DATABASE_URL }),
    plugins: [...],
});

No Table Bloat

Adding more integrations doesn't add more tables. Slack, Linear, GitHub — they all use the same four tables. Your schema stays clean no matter how many integrations you add.

Next Steps

  • PostgreSQL schema, indexes, and performance tips