Connectors Overview
Continuously ingest third-party sources into CortexDB — self-hosted with cortexdb-sync, or managed. The shared mechanics for all 18 connectors.
Connectors keep CortexDB in sync with the systems your team already uses — chat, tickets, docs, code, CRM. There are 18 connectors, and every one runs two ways:
- Self-hosted (recommended) — the
cortexdb-connectorspackage ships acortexdb-syncCLI you run yourself against your own CortexDB instance. Nothing leaves your infrastructure, and it's the path documented on each connector page and below. Point it athttp://localhost:3141(or your server's URL) and it ingests into your scopes. - Managed — alternatively, CortexDB can run the connector as a worker on its infrastructure; configure it once from your dashboard and it handles webhook registration, retries, backfill, and idempotency.
Install the CLI
pip install cortexdb-connectorsThis provides the cortexdb-sync command with six subcommands:
| Subcommand | What it does |
|---|---|
sync <connector> | One-shot sync. |
watch <connector> --interval N | Sync forever with a sleep between cycles. |
serve | Run the webhook receiver for real-time create/update/delete. |
status | Show cursor state for every connector. |
list | List available connectors and their required env vars. |
auth | Show the resolved CortexDB credentials (redacted). |
Add a connector's optional extra to pull its dependencies, e.g. pip install 'cortexdb-connectors[slack]'.
Point it at your instance
The CLI defaults to the cloud — override for self-host
cortexdb-sync targets the cloud API by default. For a self-hosted server, pass --api-url (or set
CORTEXDB_URL) to your instance, e.g. http://localhost:3141.
CortexDB credentials resolve in precedence order: CLI flags → environment → ~/.cortexdb/state.json
(written by cortexdb init). state.json supplies the token, actor, scope and tenant, but not the
server URL: cortexdb init --endpoint keeps the endpoint in the CLI's own config.toml. So for a
self-hosted server, always pass --api-url or set CORTEXDB_URL, even after cortexdb init. The
relevant global flags:
cortexdb-sync \
--api-url http://localhost:3141 \
--api-key <PASETO-or-CORTEX_API_KEY> \
--actor user:alice \
--scope-template "org:acme/source:slack/project:{channel}" \
sync slackcortexdb-sync auth prints the resolved (redacted) credentials so you can confirm what it will use.
Configure a source
Each connector needs its third-party credentials. Two ways to supply them:
- Environment variables — run
cortexdb-sync listto see exactly which are required per connector (they're therequired envcolumn on each connector page). - A YAML
--configfile keyed by connector slug — useful for selectors that aren't plain auth (e.g. Jira'sproject_keys, Teams'team_ids). Reading the file needs PyYAML (pip install pyyaml, or the[all]extra); without it the file is silently ignored with only a warning.
The scope template shapes where events land
--scope-template (or CORTEXDB_SCOPE_TEMPLATE) controls the scope each event is written to, with
per-connector placeholders like {channel}, {author}, {workspace}. See
Scopes. Every segment type must be an allowed scope type: on the hosted
cloud (and on a self-hosted server with the cloud_shared_saas preset) org, dept, team, app, user, agent, service, ws, project, source; the on_prem_enterprise, cloud_private and dev_local
presets also allow global and system (the table). A
template such as
.../channel:{channel} or .../repo:{repo} is rejected on first write with 422 UNREGISTERED_SCOPE_TYPE,
and registering the scope first is refused the same way. Put the placeholder under an
allowed type, e.g. org:acme/source:slack/project:{channel}.
Run modes
# One-shot: sync everything new since the last cursor, then exit
cortexdb-sync sync slack
# Watch: loop forever, sleeping --interval seconds between cycles
cortexdb-sync watch slack --interval 60
# Serve: receive real-time webhooks (create/update/delete)
cortexdb-sync serveCursor state persists in ~/.cortexdb/sync_state.json (per-connector), so re-running picks up where
the last cycle left off. --state-file overrides the location, and CORTEX_SYNC_STATE_PATH is the
environment equivalent — useful when the connector runs in a container with a mounted state volume.
(The packaged worker entrypoint reads SYNC_INTERVAL, default 300 seconds, for its own loop.)
There is a second piece of durable state next to it: the event ledger, a SQLite file at
~/.cortexdb/connector_event_ledger.sqlite3 (or under CORTEXDB_STATE_DIR, or exactly
CORTEX_CONNECTOR_EVENT_LEDGER_PATH). It records every CortexDB event id and blob id a connector created
per logical source, without the source text, so retries after a crash and later source erasure stay
exact. Persist it alongside the state file; a container without a mounted state volume loses both on
restart.
Waiting for derived layers
Pass --wait-derivation to have a sync block until the derived layers catch up, rather than returning
as soon as events are captured. Derived layers (Facts/Beliefs) require enrichment on a self-host — see
Self-hosting defaults.
Webhooks (real-time)
cortexdb-sync serve runs a receiver for connectors that support push. It needs the webhooks extra
(pip install 'cortexdb-connectors[webhooks]'; without it serve exits with
ModuleNotFoundError: No module named 'uvicorn') and listens on --port (default 8088) and
--host (default 0.0.0.0). Verification is per-connector:
some sources sign their payloads, while sources that don't (e.g. tl;dv) use a static shared secret
header instead. The receiver reads these environment variables:
| Env var | Default | What it does |
|---|---|---|
WEBHOOK_PORT | 8081 | Port for python -m cortexdb_connectors.webhooks only; cortexdb-sync serve ignores it and uses --port (default 8088). |
SLACK_SIGNING_SECRET | — | Verifies Slack request signatures. |
GITHUB_WEBHOOK_SECRET | — | Verifies GitHub's X-Hub-Signature-256 header. |
JIRA_WEBHOOK_SECRET | — | HMAC secret: verifies Jira's X-Hub-Signature (sha256=<hex>) body signature. |
FRESHDESK_WEBHOOK_SECRET | — | Static shared secret for Freshdesk deliveries. |
TLDV_WEBHOOK_SECRET | — | Sent by tl;dv as Authorization: Bearer <secret>. |
A secret left unset makes the receiver reject every delivery for that source (fail-closed, 401) — set one for every connector you actually expose. Individual connector pages cover the webhook path and payload details.
Cross-connector notes
preceded_by is not set by any connector
Connector pages sometimes describe thread replies linking via context.preceded_by. No connector
sets preceded_by — a connector doesn't have the parent's CortexDB evt_… id at emit time. Instead
it attaches a thread=<source-specific-id> label, which you can filter recall by. Each connector page
lists its thread label.
- Idempotency keys are per-connector and version-aware where the source supports edits (a
content-version or
updatedtimestamp segment lets an edit land as a new version rather than colliding). The shape on each connector page is the connector's internal record identity; the key actually sent to CortexDB is derived from it (see below), so an edit still lands as a new event and a redelivery still replays. - Self-host auth may differ from the dashboard. Some connectors use OAuth apps in the managed dashboard but simpler token/password auth self-hosted (GitHub, Salesforce, ServiceNow). Each page notes both.
Sync behaviour
- GitHub stores the full content: pull-request descriptions, push commits, issue bodies, full comments,
review text, and inline review comments with their file, line and diff hunk. It backfills history
(
GITHUB_BACKFILL_DAYS, default 30) and re-reads the last 6 hours on each later sync so late events aren't skipped.GITHUB_REPOSis required. Webhooks (serve) build the same content as polling. See GitHub. cortexdb-sync listprints each connector's optional settings next to the required ones.- Given-up items stay given up. An item abandoned after the dead-letter budget is not re-read when a lookback window revisits it. Connectors that keep their own state (GitHub) save it with the cursor in the state file.
- Actor and key. The packaged worker entrypoint adopts
user:localas the actor on a static-key server. The one-shotcortexdb-sync syncneedsCORTEXDB_ACTOR, and every command needsCORTEXDB_API_KEY, even against a server running without auth (any placeholder works there).
Source identity and keys
- Every event carries a
sourcedescriptor: the canonical source block with a stablesource_id(connector:<system digest>:<record digest>), the content kind, asha256:fingerprint of the exact bytes, and a fragment locator with abyte_range. Recall andGET /v1/eventsreturn it, so a citation can be traced to the exact upstream record. - The wire
idempotency_keyiscxsrc:<56 hex>. It is derived from the scope, the canonical source id, the content fingerprint and the fragment range. The source-facing key each page documents (for examplejira:{issue_key}:{updated}) decides identity and versioning, but it is not what appears on the event. A re-sync or webhook redelivery of unchanged content is skipped locally (the ledger already holds its event id), so it cannot duplicate even after the server's 24 h idempotency window; changed content gets a new fingerprint and therefore a new event; andGET /v1/experience/status?idempotency_key=needs thecxsrc:value from the connector log, not the source-facing one. - Very long content is split without loss. Oversized text becomes several envelopes, each with its
own fragment
byte_rangeand its own key; the ledger records all of them as one logical source. - Blob uploads are idempotent. Attachments go through
POST /v1/blobs/idempotentwith a ledger-persisted upload token, so a retried upload cannot duplicate a blob. - Two global flags set the identity.
--source-instance-id(envCORTEXDB_SOURCE_INSTANCE_ID): a stable, non-secret upstream account or site id, required when several accounts of one provider share an endpoint, actor and scope, because it becomes part of the source identity.--cortex-tenant-id(envCORTEXDB_TENANT_ID): the billing tenant you expect; it is checked against/v1/auth/whoamibefore any durable ledger state is used, which prevents a ledger built against one tenant from being reused against another. - Exact source erasure. Because the ledger holds the full set of event and blob ids per source, the
connector library's forget path (
forget_episode) can remove one source's events exactly, fragments and attachments included. It is a library call;cortexdb-synchas no delete subcommand.
Upgrading from an older connector release? See Upgrading.
The 18 connectors
| Connector | Required env |
|---|---|
| Slack | SLACK_BOT_TOKEN |
| GitHub | GITHUB_TOKEN, GITHUB_REPOS |
| GitLab | GITLAB_TOKEN |
| Jira | JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN |
| Freshdesk | FRESHDESK_DOMAIN, FRESHDESK_API_KEY |
| tl;dv | TLDV_API_KEY |
| Linear | LINEAR_API_KEY |
| Confluence | CONFLUENCE_URL, CONFLUENCE_EMAIL, CONFLUENCE_API_TOKEN |
| Notion | NOTION_TOKEN |
| PagerDuty | PAGERDUTY_API_KEY |
| Discord | DISCORD_BOT_TOKEN |
| Microsoft Teams | TEAMS_TENANT_ID, TEAMS_CLIENT_ID, TEAMS_CLIENT_SECRET |
| Google Workspace | GW_SERVICE_ACCOUNT_KEY, GW_DELEGATED_USER |
| Salesforce | SF_INSTANCE_URL, SF_CLIENT_ID, SF_CLIENT_SECRET, SF_USERNAME, SF_PASSWORD |
| HubSpot | HUBSPOT_TOKEN |
| Zendesk | ZENDESK_SUBDOMAIN, ZENDESK_EMAIL, ZENDESK_TOKEN |
| Intercom | INTERCOM_TOKEN |
| ServiceNow | SNOW_INSTANCE, SNOW_USERNAME, SNOW_PASSWORD |