Aller au contenu principal

Connectors (Chat, Trackers, Docs, CRM, Meetings & Social)

A connector is a plugin that plugs an outside system into Ever Works: it pulls that system's activity in as normalized events, and — for the ones that can write back — it lets an Agent post a message, a comment, a note or a record out.

Eleven connectors ship today, in the connector plugin category. This page is the catalog: what each one carries, what credentials it needs, and what it does not do. Integrations covers the two receivers that are not connectors (the Slack app and GitHub pull-request review) and the shape of the event envelope itself.

Two legs, two capabilities

A connector declares up to two independent things, and the difference matters when you read the tables below.

  • The connector capability is the messaging leg — sending outbound, and (for Slack) accepting inbound control. Its direction is outbound, inbound or bidirectional.
  • The event-source capability is the ingest leg — a pullEvents sweep that feeds the event spine on a cron.

Most connectors declare both. A connector can be direction: 'outbound' and still pull a rich event stream — Linear is exactly that.

The catalog

ConnectorPlugin idconnector directionevent-sourcebackfillDaysPulls inPushes out
Slackslack-connectorbidirectionalyesnoChannel messages and bot mentions from the configured channels (slack.message, slack.mention)Channel messages and threaded replies, Block Kit supported
Discorddiscord-connectoroutboundnon/aNothing — this connector has no ingest legChannel messages with embeds, via the discord.js REST client
Linearlinear-connectoroutboundyesyesIssues created/updated and comments (linear.issue, linear.comment)Comments on Linear issues
Notionnotion-connectoroutboundyesyesPages created or edited, workspace-wide or per database (notion.page)Comments appended to Notion pages
Jirajira-connectoroutboundyesyesIssues created/updated and comments (jira.issue, jira.comment)Comments on Jira issues
HubSpothubspot-connectoroutboundyesyesContacts, companies, deals and custom objects (hubspot.contact, .company, .deal, .record)Notes appended to CRM records, and new records
Pipedrivepipedrive-connectoroutboundyesyesDeals, persons and organizations (pipedrive.deal, .person, .organization)Notes on deals/persons/organizations, and new records
Zoomzoom-connectorinboundyesyesCompleted cloud recordings with transcripts when available (zoom.recording)Nothing — ingest only
Google Workspacegoogle-workspace-connectorinboundyesyesDrive file changes, Calendar events, Meet recording transcripts (google.drive-change, google.calendar-event, google.meet-recording)Nothing — ingest only
Blueskybluesky-connectoroutboundyesyesMentions and replies, plus the connected account's own posts (bluesky.notification, bluesky.post)Posts and threaded replies over AT Protocol
Mastodonmastodon-connectoroutboundyesyesMention notifications and the account's own statuses (mastodon.notification, mastodon.status)Statuses and threaded replies on your own instance
Discord is outbound only

The Discord connector declares direction: 'outbound' with inbound: false and reply: false, and it does not declare the event-source capability. It posts into a channel; it does not read one, and nothing from Discord reaches your Activity feed. Inbound Interactions API routing is a documented follow-up — the publicKey setting is on the manifest for it, unused today.

Every connector uses the provider's own official SDK: @slack/web-api, discord.js, @linear/sdk, @notionhq/client, jira.js, @hubspot/api-client, the pipedrive Node SDK, @zoom/rivet, the Google API Node.js clients, @atproto/api and masto.

What "pushes out" means today

The Pushes out column is the capability each plugin implements, and the ingest leg is what the platform drives on its own. The generic connector routing runtime — an inbound message routed to an Agent or Team, which then replies through the same connector — is wired for Slack only at present; the Slack app receiver below is that wiring. If what you want is routine outbound delivery (alerts, digests, run results into a channel), reach for a notification channel instead: Slack, Discord, Telegram, WhatsApp and Novu ship as an outbound-only plugin family with their own Settings screen and a test-send button.

What each connector needs

Fill these in on the plugin's settings form. Bold fields are required by the plugin's manifest: without them the connector reports itself not configured and its sweep is skipped — quietly and deliberately, rather than erroring on every tick.

ConnectorRequiredOptional / scoping
SlackBot User OAuth token (xoxb-…)Signing secret (verifies inbound Events API deliveries), app id, default channel id, eventChannelIds — the channels to ingest from, comma-separated; defaults to the default channel
DiscordBot tokenApplication id, default guild id, default channel id, application public key (reserved for inbound)
LinearAPI key (lin_api_…)teamIds to ingest from (all teams when empty), backfillDays, default issue id for outbound comments
NotionIntegration token (ntn_… / secret_…)databaseIds (workspace-wide search when empty), backfillDays, default page id for outbound comments
JiraSite base URL, Atlassian account email, API tokenprojectKeys (all projects when empty), backfillDays, default issue key (e.g. ENG-42)
HubSpotPrivate-app access token (pat-…)objectTypes (defaults to contacts,companies,deals), portal id — enables deep links back to each record, backfillDays, default object type, default associated record id
PipedriveAPI tokenentityTypes (defaults to deals,persons,organizations), company domain — enables deep links, backfillDays, default deal id
ZoomAccount id, client id, client secret (Server-to-Server OAuth app)backfillDays
Google WorkspaceOAuth client id, client secret, refresh token (scopes drive.readonly + calendar.readonly)surfaces (drive, calendar, or both — the default), driveFolderIds, calendarIds (defaults to primary), meetTranscripts (on by default), backfillDays
BlueskyHandle or DID, app passwordPDS service URL (defaults to https://bsky.social), backfillDays
MastodonInstance URL, access tokenDefault visibility for outbound statuses (public), backfillDays
Use an app password, not your account password

The Bluesky connector's appPassword field is for a Bluesky app password. The Mastodon connector talks only to the instance URL you configure, and that URL is SSRF-guarded — it will not follow a redirect to somewhere else on your network.

How to enable a connector

  1. Open Sidebar → Plugins (/plugins), or go straight to Settings → Plugins → Connectors (/settings/plugins/connector). Filter with Search plugins… if the catalog is long.
  2. Press Enable on the connector's card. A dialog opens carrying the Also enable for all works checkbox — leave it ticked so your Works actually run the plugin, then press Enable again to commit. See Plugins for why account-level on does not cascade on its own.
  3. Open the connector's settings form, paste the credentials from the table above, and press Save Settings.
  4. Scope the sweep while you are there. eventChannelIds, teamIds, databaseIds, projectKeys, objectTypes, entityTypes, driveFolderIds, calendarIds and surfaces all narrow what gets pulled — an empty value usually means "everything", which is rarely what you want on a large workspace.
  5. Wait for the next sweep. The event-ingest-tick cron runs every 5 minutes; there is no "sync now" button.
  6. Check the results on a Work's Activity tab (/works/:id/activity) or on Sidebar → Activity (/activity).
There is no Settings → Integrations page

/settings/integrations has no index page and soft-404s — the settings navigation deliberately omits a bare Integrations tab. Only its two children exist: Channels (/settings/integrations/channels) and Emails (/settings/integrations/emails), both for Notifications. Connectors live under Plugins. Note also that bare /settings/plugins redirects to /settings/plugins/ai-provider, so link to /settings/plugins/connector when you mean this catalog.

Routing events to a Work

A connector sees Slack channels and Jira projects; it has never heard of your Work ids. So it attaches a work hint — the container's id in the source system — and the platform resolves that hint against your own Works only.

Hint kindWhat it carriesConnectors that emit it
chat-channelChannel id (e.g. C0123456789)Slack
tracker-teamTeam key or project keyLinear, Jira
doc-databaseDatabase or parent folder idNotion, Google Workspace (Drive)
meetingMeeting or conference idZoom, Google Workspace (Calendar / Meet)
repoowner/repoGit receivers — resolved through the Work's declared repositories, not a hand-kept map

To claim a container for a Work:

  1. Open the Work and go to its Settings tab (/works/:id/settings).
  2. Find the Ingest routing claims panel — "Claim the external containers whose events belong to this Work."
  3. Type the source-system id into the matching group — Chat channels, Tracker teams, Doc databases or Meetings — and press Add. Repositories are deliberately absent: repository events already route through the repositories the Work declares.
  4. Press Save claims. Ids are compared case-insensitively and trimmed; each is capped at 200 characters, with at most 50 ids per kind.

An unresolved hint is not an error. The event simply stays user-scoped — it still lands, it still reaches your Activity feed and Memory, it just is not filed under a Work. HubSpot, Pipedrive, Bluesky and Mastodon emit no hint at all today, so their events are always user-scoped.

The event spine underneath

Every connector feeds one pipeline. Nothing about that pipeline is per-provider.

Pull. The cron resolves, for every loaded event-source plugin, the users who enabled it, resolves their settings, and calls pullEvents with a persisted per-(user, plugin) watermark and continuation cursor. A sweep that exhausts its page budget — 5 pages per pair per tick — saves its cursor and resumes on the next tick with the same watermark. On completion the watermark advances to when the sweep started, never to "now", so events that landed mid-sweep are re-covered next time.

Push. POST /api/ingest/events takes a batch of envelopes for the authenticated caller. It is the same road: pushed and pulled events are indistinguishable downstream.

GuaranteeDetail
Dedupe identity(source, sourceEventId) per owner. Re-delivery is free — retries, overlapping windows and backfills all collapse.
Batch cap100 envelopes per call.
Payload cap32 KB serialized per envelope, enforced at the edge: an over-cap batch or an oversized payload fails the whole push call with 400.
rejected countCounts envelopes a connector's own pull sweep produced that the ingest floor turned away on shape or size — a bad envelope is counted, never allowed to fail the sweep.
Response202 with { inserted, duplicates, rejected, filtered }. filtered is always 0 unless an operator configured the salience filter.
Rate limit120 calls per minute on the push endpoint.
OwnershipEvents land owner-scoped under the caller. Asking for a Work you do not own returns an empty page, never someone else's rows.
Failure isolationEach (plugin, user) pair pulls inside its own try/catch. One broken connector, or one user's revoked credentials, never stops the batch.

Reading events back is GET /api/ingest/events, filterable by workId and source, limit defaulting to 20 and capped at 100.

Install bindings

Inbound receivers have to answer a question a pull never asks: whose account does this delivery belong to? The ingest_install_bindings table is the answer — one row per external workspace or installation, naming the owning platform user and the plugin that serves it.

Resolution runs in a fixed order: an exact binding wins; otherwise a single configured install is used and the binding is recorded once the delivery passes signature verification, so the deployment self-migrates onto the first path; otherwise a unique cryptographic signature match counts as proof of ownership; otherwise the receiver refuses. A refusal is a clean no-op — a warning log, HTTP 200, nothing ingested and nothing dispatched. Never a guess, never a 500. GitHub adds one more path: a delivery verified against the platform GitHub App's webhook secret is attributed to whoever installed the App, so installing it needs no second setup step.

Where the events show up

SurfaceRoute / callWhat you see
Work → Activity/works/:id/activityThe External activity panel below the platform feed — "Events ingested from the connectors for this Work — repos, trackers, docs, chat and meetings." Source filter chips start at All sources and are derived from the events actually present, so a newly enabled connector appears without a code change; each row deep-links out via sourceUrl.
Global Activity/activityIngested events fan out to the Activity log. github.push, github.commit and github.merge get dedicated git action types; everything else lands as an external-event entry carrying its sourceUrl.
Memory/memoryA best-effort memory observation per event, tagged with its provenance. No memory provider enabled simply means no observation — it never fails the ingest.
Meetings/memory#meetingszoom.recording and google.meet-recording envelopes are turned into Meeting rows by a kind processor that runs before the Activity write, so a failure retries instead of duplicating feed rows.
Agents and chatlist_recent_events toolAgents can list your recent ingested events, filtered by source or workId (default 20, capped at 50), and cite each one by its sourceUrl.

Historical backfill

By default a connector's first sweep starts from the epoch watermark and each connector interprets that as "start now" — you get new activity, not history. Nine of the eleven accept a backfillDays setting to widen that first pull instead.

SettingBehaviour
0 (default)Backfill off.
190The first pull reaches that many days back.
Anything elseClamped. Negatives, NaN and unparseable values all resolve to 0 — a garbage window can never widen the sweep.

The ceiling is 90 days, shared by every connector so the answer to "how far back can I go?" is the same everywhere. Slack has no backfillDays setting; Discord has no ingest leg at all.

What is shipped, and what is not

Linear, Notion, Jira, Zoom and Google Workspace additionally implement the capability's optional backfill() method — a bounded, out-of-band historical sweep with no watermark side effects, budgeted at 20 pages per run and resumable by cursor. The service that drives it exists, but no REST route or dashboard button calls it yet: today the reachable lever is the backfillDays setting, applied on a connector's first pull. Set it before the first sweep, not after.

Slack and GitHub also have dedicated receivers

Two integrations do more than a connector can, and they are wired separately:

  • The Slack appPOST /api/ingest/slack/events and POST /api/ingest/slack/commands. Mention @works in a channel or type /works <question> and the message is routed into the same platform chat the web app uses; the answer is posted back into the thread. This is the inbound leg of the Slack connector, and it is the only connector wired into that routing runtime today.
  • GitHub pull-request reviewPOST /api/ingest/github/events, distinct from the platform GitHub App webhook at /api/github-app/webhooks.

Both verify every delivery by HMAC and fail closed: with no configured install, everything is rejected — including Slack's own url_verification handshake, which Slack signs like any other delivery. Full details in Integrations.

Third-party trigger aggregators

Connectors are first-party and speak the event spine directly. When the system you need has no connector, two other routes exist — and they are genuinely different things, so pick deliberately.

Composio triggers

The Composio integration is the one aggregator with its own trigger API. Subscriptions are created against Composio's own trigger catalog and stored keyed by the returned tg_* id.

EndpointAuthWhat it does
GET /api/plugins/composio/triggersSessionYour trigger subscriptions, with delivery counters and lastFiredAt.
POST /api/plugins/composio/triggersSessionEnables the trigger upstream on Composio, then persists the subscription keyed by the real tg_* id.
DELETE /api/plugins/composio/triggers/:idSessionRemoves the local row, then tears the trigger down upstream (best-effort).
POST /api/plugins/composio/webhookPublicThe receiver. Resolves the subscription by the tg_* id in the payload, then verifies the delivery through the Composio SDK using your project webhook secret and the webhook-id / webhook-signature / webhook-timestamp headers.

The webhook secret is configured under Settings → Plugins → Composio. Verification fails closed when no secret is set: 401 for a bad signature, 404 for an unknown trigger id (returned without a body, so the endpoint does not disclose which triggers exist), 200 on accept so Composio does not retry.

Deliveries are recorded, not yet fanned out

An accepted delivery increments the subscription's deliveriesReceived and lastFiredAt counters — enough to show the trigger is alive. Handing the payload onward into the event spine is an explicit follow-up. Use a first-party connector when you need the event on your Activity feed today.

Pipeline plugins, not event sources

make, zapier, activepieces and sim-ai are pipeline-category plugins: they delegate steps of Work generation out to a Make scenario, a Zapier action, an Activepieces flow or a SIM AI workflow. They do not ingest events and they do not appear in the connector catalog. The composio plugin is a pipeline plugin too, separate from the trigger API above — it executes Composio tools across third-party apps during generation, brokering OAuth so each user connects their own accounts once.

See Composio, Make, Zapier, Activepieces and SIM AI.

Twenty CRM

Twenty CRM is wired differently from everything above: it is an API-only, environment-configured integration, not a plugin, and it has no dashboard screen.

AspectDetail
Configured byEnvironment variables: TWENTY_CRM_BASE_URL, TWENTY_CRM_API_KEY, TWENTY_CRM_WORKSPACE_ID. Optional TWENTY_CRM_TIMEOUT_MS, TWENTY_CRM_MAX_RETRIES, TWENTY_CRM_RETRY_DELAY_MS, and TWENTY_CRM_TENANTS for per-tenant credential overrides.
Enabled whenAll three of base URL, API key and workspace id are set. Any missing one leaves the integration off.
Routes/api/twenty-crm/companies — list, get, create, update, delete. Session-authenticated and behind a sync guard.
Not mounted/api/twenty-crm/people exists in the source but is not registered; it returns 404.
No status routeThere is no config, status or health endpoint — those paths 404. The gate is the only signal.
Unconfigured behaviourFails closed: an authenticated request to the companies routes returns 403 rather than a partial result.
No UINothing in the dashboard references it. Drive it from the API.

Reference details live in Twenty CRM API and Integrations module.

Troubleshooting

SymptomWhere to look
Enabled the connector, nothing arrivesThe sweep runs every 5 minutes — give it one tick. Then confirm the plugin is enabled and its credentials saved; an enabled-but-unconfigured source is a deliberate quiet no-op, not an error.
Events arrive but are not on the WorkThe work hint did not resolve. Claim the channel id / team key / database id / meeting id on the Work's Settings tab, and remember HubSpot, Pipedrive, Bluesky and Mastodon emit no hint at all.
Nothing from Discord ever appearsExpected. Discord is outbound only.
The same event appears twiceIt should not — dedupe is on (source, sourceEventId) per owner. Two different accounts legitimately ingesting the same source event is a separate row for each, by design.
A Slack delivery is rejected with 401The receiver fails closed. Either no install is configured, the signing secret does not match, or the delivery came from a workspace no account has connected.
History is missing after enablingbackfillDays only widens the first pull. Once the watermark has advanced, changing it does nothing.
Only some Drive files or calendars show upCheck surfaces, driveFolderIds and calendarIds — the connector ingests exactly what they scope it to.