Docs › Integrations

Connections — Slack integration

Connections is Fair's shared integration layer. Let your end-users connect Slack, then send them typed alerts and receive their messages — without your app ever owning OAuth, token encryption, signature verification, or rate limits. You speak in tenants and alert types; Fair owns everything provider-specific. The same contract serves Teams and Discord later behind the same shape.

What's live today

Connections ships in phases. This page marks each capability so you build against what's real:

  • LIVE Connections & Directory — connect a tenant's Slack workspace (BYO app + validation), then list channels.
  • COMING Ingest, Post-with-routing, and Interactions — read the normalized message stream, post typed/threaded alerts, and receive Block Kit action callbacks. Endpoint shapes are documented below so you can design ahead, but they are not yet available.

Authentication

Same pattern as the rest of Fair — a project-scoped Bearer token from tryfair.ai/api-keys on every request:

Authorization: Bearer <FAIR_API_KEY>

tenantId is your stable per-end-user identifier (namespaced per workspace, exactly like user_ref in the FairMail Agent surface). One consistent Fair integration story across mail and messaging. The consumer never sees a Slack token, scope, or API call.

Base URL (prod): https://api.faircompany.ai
Base URL (staging): https://staging.api.faircompany.ai

Bring-your-own Slack app

Each tenant brings their own Slack app — the bot is the tenant's own (their branding, their ownership), there's no Slack Marketplace review for internal apps, and two consumers can own the same workspace. Fair makes creation trivial and then validates it:

  1. Fair returns a pre-filled app manifest — scopes and Fair's redirect/events URLs already baked in — so the tenant creates their app in ~2 clicks.
  2. Fair validates the app with 4 checks: credentials exchange works, scopes are complete, the signing secret verifies, and the URLs point at Fair.
  3. Per-tenant client_id / secret / signing_secret + bot token are stored encrypted at rest and resolved in-process only. The raw token never leaves Fair.

Fetch the manifest LIVE

curl https://api.faircompany.ai/v1/connections/slack/manifest \
  -H "Authorization: Bearer $FAIR_API_KEY"

Begin an install LIVE

curl -X POST https://api.faircompany.ai/v1/connections/slack/install \
  -H "Authorization: Bearer $FAIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tenantId": "tenant_123",
        "returnUrl": "https://yourapp.com/settings/slack" }'
200 {
  "manifest": { "display_information": { "name": "…" }, "…": "…" },
  "oauthUrl": "https://api.faircompany.ai/v1/connections/slack/setup?state=…"
}

Redirect the tenant to oauthUrl; Fair carries a signed state and returns them to your returnUrl when the install completes.

Validate the app LIVE

curl -X POST https://api.faircompany.ai/v1/connections/slack/tenant_123/validate \
  -H "Authorization: Bearer $FAIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "clientId": "…", "clientSecret": "…", "signingSecret": "…" }'
200 {
  "checks": {
    "credentials": true,   // client id/secret exchange works
    "scopes": true,        // all required scopes present
    "signingSecret": true, // inbound signature verifies
    "urls": true           // redirect + events URLs point at Fair
  },
  "status": "validated"
}

The 5-capability contract

Everything Connections does collapses to five capabilities behind one contract. Paths are keyed by provider (/slack//teams//discord/ later — same shape).

1 · Connections LIVE

Fair owns OAuth, return-origin HMAC safety, token encryption, and scope/manifest management. Install, check status, disconnect:

curl https://api.faircompany.ai/v1/connections/slack/tenant_123 \
  -H "Authorization: Bearer $FAIR_API_KEY"
200 { "status": "connected",
      "workspace": { "id": "T…", "name": "Acme HQ" },
      "health": "ok" }
curl -X DELETE https://api.faircompany.ai/v1/connections/slack/tenant_123 \
  -H "Authorization: Bearer $FAIR_API_KEY"

2 · Directory LIVE

curl https://api.faircompany.ai/v1/connections/slack/tenant_123/channels \
  -H "Authorization: Bearer $FAIR_API_KEY"
200 { "channels": [
  { "id": "C123", "name": "updates",   "isMember": true,  "isPrivate": false },
  { "id": "C456", "name": "approvals", "isMember": false, "isPrivate": false }
] }

3 · Ingest (read) COMING

Fair auto-joins, listens, stores, and pushes a normalized, provider-agnostic stream to your callback:

webhook → your callback:
{ "event": "message.received",
  "tenantId": "tenant_123", "channel": "C123",
  "author": "U456", "text": "hey", "ts": "…", "threadTs": "…" }

4 · Post with routing COMING

You send a type, never a channel — Fair resolves type → channel from a routing table you set. Thread replies via an optional inReplyTo.

PUT /v1/connections/slack/:tenantId/routing
{ "default": "#updates",
  "rules": { "approval": "#approvals", "digest": "#weekly", "client_alert": "#cs" } }
POST /v1/connections/slack/:tenantId/messages
{ "type": "approval", "text": "Deploy v42 to prod?",
  "actions": [ { "id": "approve", "label": "Approve", "style": "primary" },
               { "id": "reject",  "label": "Reject",  "style": "danger" } ] }
→ { "messageId": "…" }   // Fair resolves type → channel

5 · Interactions (write → callback) COMING

Block Kit button clicks come back normalized — not as a raw Slack payload:

webhook → your callback:
{ "event": "action.invoked",
  "tenantId": "tenant_123", "messageId": "…",
  "actionId": "approve", "actorId": "U456" }

Endpoint summary

Live now

Method Path What it does
GET /v1/connections/slack/manifest The pre-filled Slack app manifest + Fair URLs
POST /v1/connections/slack/install Begin an install → { manifest, oauthUrl }
POST /v1/connections/slack/:tenantId/validate The 4-check app validation
GET /v1/connections/slack/:tenantId Connection status, workspace, health
DELETE /v1/connections/slack/:tenantId Disconnect (revoke + clean)
GET /v1/connections/slack/:tenantId/channels Directory — list channels

Coming (P2 / P3)

Method Path What it will do
GET /v1/connections/slack/:tenantId/messages Normalized message history (ingest)
POST /v1/connections/slack/:tenantId/backfill Resumable history pull → job to poll
PUT /v1/connections/slack/:tenantId/routing Store type → channel routing table
POST /v1/connections/slack/:tenantId/messages Post a typed / threaded message
PUT /v1/connections/slack/:tenantId/callback Where Fair POSTs normalized events

The authoritative, always-current endpoint reference is the live Swagger doc: api.faircompany.ai/v1/docs/ui.

Provider-agnostic by construction

The division is deliberate: Fair owns the transport — tokens, scopes, rate limits, conversations.*, webhooks, normalization. You own the intent — which alert types exist, when to fire, routing defaults, and the portal UI. Because "alert type → destination" doesn't care about the channel tech, the same five capabilities will serve Teams, Discord, and email later behind the same contract. Slack is simply the first provider adapter.

Support

Questions? Email hello@faircompany.ai.