Skip to main content
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