Skip to main content

Overview

Adapt integrates with Slack using OAuth 2.0 to send direct message notifications when crawl jobs complete or fail. Connect your Slack workspace and link your user account to start receiving updates. Key Features:
  • OAuth 2.0 workspace connection
  • Direct message notifications per user
  • Automatic user linking via email matching
  • Configurable notification preferences
  • Organisation-wide workspace sharing

OAuth Flow

Initiating Connection

Start the Slack OAuth flow:
POST /v1/integrations/slack
Authorization: Bearer <your_jwt_token>
Response:
{
  "status": "success",
  "data": {
    "auth_url": "https://slack.com/oauth/v2/authorize?client_id=..."
  },
  "message": "Redirect to this URL to connect Slack"
}
Redirect the user to the auth_url to authorize the app.

Required OAuth Scopes

From internal/api/slack.go:61-62, Adapt requests:
  • chat:write - Send messages to channels
  • im:write - Send direct messages to users
  • users:read - List workspace members
  • users:read.email - Match users by email address
const (
    slackOAuthScopes = "chat:write,im:write,users:read,users:read.email"
    slackAPITimeout = 30 * time.Second
)

OAuth Callback

Slack redirects to:
https://your-domain.com/v1/integrations/slack/callback?code=xxx&state=xxx
Adapt handles this automatically (internal/api/slack.go:238-313):
1

Validate State

Verify the HMAC-signed state parameter to prevent CSRF attacks.
2

Exchange Code

Trade the authorization code for an access token using Slack’s OAuth v2 endpoint.
3

Store Connection

Save the workspace connection with workspace ID, name, and bot user ID.
4

Secure Token

Store the bot access token in Supabase Vault (encrypted at rest).
5

Redirect

Return to settings page with success status and connection ID for auto-linking.

Connection Storage

From internal/api/slack.go:281-296:
now := time.Now().UTC()
conn := &db.SlackConnection{
    ID:               uuid.New().String(),
    OrganisationID:   state.OrgID,
    WorkspaceID:      resp.Team.ID,
    WorkspaceName:    resp.Team.Name,
    BotUserID:        resp.BotUserID,
    InstallingUserID: &state.UserID,
    CreatedAt:        now,
    UpdatedAt:        now,
}

User Linking

Automatic Linking

When you connect a Slack workspace, Adapt attempts to link your user account automatically:
  1. Email Matching - Looks up your Slack user ID using your email address
  2. Profile Data - Uses slack_user_id from your Supabase profile if available
  3. Manual Override - Allows explicit Slack user ID in the request body
POST /v1/integrations/slack/{connection_id}/link-user
Authorization: Bearer <your_jwt_token>
Content-Type: application/json

{
  "slack_user_id": "U01234ABCDE",  // Optional - will auto-detect if omitted
  "dm_notifications": true
}
Response:
{
  "status": "success",
  "data": {
    "id": "link_xyz789",
    "slack_user_id": "U01234ABCDE",
    "dm_notifications": true,
    "created_at": "2024-03-03T12:00:00Z"
  },
  "message": "Slack user linked successfully"
}

Email Lookup Logic

From internal/api/slack.go:511-529:
// If still no Slack user ID, try to find by email
if slackUserID == "" && userClaims.Email != "" {
    token, err := h.DB.GetSlackToken(r.Context(), connectionID)
    
    httpClient := &http.Client{Timeout: slackAPITimeout}
    client := slack.New(token, slack.OptionHTTPClient(httpClient))
    slackUser, err := client.GetUserByEmail(userClaims.Email)
    
    if err != nil {
        BadRequest(w, r, "Could not find your Slack user. Make sure your email matches your Slack account.")
        return
    }
    
    slackUserID = slackUser.ID
}
Email matching requires your Slack workspace email to match your Adapt account email.

List Workspace Users

To manually select a user, fetch the workspace member list:
GET /v1/integrations/slack/{connection_id}/users
Authorization: Bearer <your_jwt_token>
Response:
{
  "status": "success",
  "data": [
    {
      "id": "U01234ABCDE",
      "name": "john.doe",
      "real_name": "John Doe",
      "email": "[email protected]",
      "display_name": "John"
    }
  ]
}
GET /v1/integrations/slack/{connection_id}/user-link
Authorization: Bearer <your_jwt_token>
Returns your current link status or 404 if not linked.
DELETE /v1/integrations/slack/{connection_id}/link-user
Authorization: Bearer <your_jwt_token>
Removes your user link. You’ll stop receiving notifications.

Notification Preferences

Update Settings

Toggle DM notifications on or off:
PUT /v1/integrations/slack/{connection_id}/link-user
Authorization: Bearer <your_jwt_token>
Content-Type: application/json

{
  "dm_notifications": false
}
From internal/api/slack.go:592-620, this updates your notification preferences without unlinking.
Setting dm_notifications: false stops all notifications but preserves your user link. Re-enable anytime without re-linking.

Connection Management

List Connections

GET /v1/integrations/slack
Authorization: Bearer <your_jwt_token>
Response:
{
  "status": "success",
  "data": [
    {
      "id": "conn_abc123",
      "workspace_id": "T01234ABCDE",
      "workspace_name": "Acme Corp",
      "created_at": "2024-03-03T10:00:00Z"
    }
  ]
}

Get Connection Details

Retrieve connection info with your user link status:
GET /v1/integrations/slack/{connection_id}
Authorization: Bearer <your_jwt_token>
Response:
{
  "status": "success",
  "data": {
    "connection": {
      "id": "conn_abc123",
      "workspace_id": "T01234ABCDE",
      "workspace_name": "Acme Corp",
      "created_at": "2024-03-03T10:00:00Z"
    },
    "user_link": {
      "id": "link_xyz789",
      "slack_user_id": "U01234ABCDE",
      "dm_notifications": true,
      "created_at": "2024-03-03T12:00:00Z"
    }
  }
}

Delete Connection

Disconnect the Slack workspace:
DELETE /v1/integrations/slack/{connection_id}
Authorization: Bearer <your_jwt_token>
This removes:
  • The workspace connection
  • All user links for that workspace
  • The stored bot token from Vault
Deleting a connection affects all users in your organisation. They’ll need to reconnect and re-link their accounts.

Notification Format

When a crawl job completes or fails, Adapt sends a direct message: Job Completed:
✅ Crawl Complete: example.com

150 pages crawled in 2m 34s
• 145 successful
• 3 not found (404)
• 2 server errors

View results: https://adapt.app.goodnative.co/jobs/job_123abc
Job Failed:
❌ Crawl Failed: example.com

Error: Connection timeout after 30s
Attempts: 3/3

View details: https://adapt.app.goodnative.co/jobs/job_123abc
The notification format is optimised for mobile devices with clear status indicators and direct links.

Security

Token Storage

Bot access tokens are stored in Supabase Vault (encrypted at rest):
if err := h.DB.StoreSlackToken(r.Context(), conn.ID, resp.AccessToken); err != nil {
    h.redirectToSettingsWithError(w, r, "Slack", "Failed to secure connection", "notifications", "slack")
    return
}
From internal/api/slack.go:299-303.

State Validation

OAuth state parameters are HMAC-signed using SUPABASE_JWT_SECRET:
state, err := h.generateOAuthState(userClaims.UserID, orgID)
if err != nil {
    InternalError(w, r, err)
    return
}
From internal/api/slack.go:218-223.

API Rate Limits

The Slack API client uses a 30-second timeout for all requests to prevent hanging operations.

Environment Variables

# Required for OAuth
SLACK_CLIENT_ID=your_client_id
SLACK_CLIENT_SECRET=your_client_secret
SLACK_REDIRECT_URI=https://your-domain.com/v1/integrations/slack/callback

# Required for state signing
SUPABASE_JWT_SECRET=your_jwt_secret

# App URL for notification links
APP_URL=https://your-domain.com

Common Issues

Email Match Fails

Symptom: User linking fails with “Could not find your Slack user” Cause: Email in Slack doesn’t match your Adapt account email Solution: Manually select your Slack user from the workspace members list

No Notifications Received

Causes:
  1. User not linked - check link status with GET /user-link
  2. DM notifications disabled - check dm_notifications setting
  3. Bot removed from workspace - reconnect the workspace
Solution: Verify link status and notification settings

Multiple Workspaces

You can connect multiple Slack workspaces to a single Adapt organisation. Link your user account in each workspace to receive notifications across all of them.

API Reference

EndpointMethodDescription
/v1/integrations/slackPOSTInitiate OAuth flow
/v1/integrations/slackGETList connections
/v1/integrations/slack/{id}GETGet connection details
/v1/integrations/slack/{id}DELETERemove connection
/v1/integrations/slack/{id}/link-userPOSTLink your account
/v1/integrations/slack/{id}/link-userPUTUpdate notifications
/v1/integrations/slack/{id}/link-userDELETEUnlink your account
/v1/integrations/slack/{id}/user-linkGETGet link status
/v1/integrations/slack/{id}/usersGETList workspace members

Next Steps

Webflow Integration

Auto-crawl on publish events

Custom Webhooks

Build your own integrations

Build docs developers (and LLMs) love