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.
https://api.faircompany.ai 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:
- 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.
- Fair validates the app with 4 checks: credentials exchange works, scopes are complete, the signing secret verifies, and the URLs point at Fair.
-
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.
See also
FairMail Agent guide · Fair API overview · Swagger reference
Support
Questions? Email hello@faircompany.ai.