# Sources

> Everything that can feed your event log, how each connects, and the events it records.

Connect sources on **Connections**, or with `configure` (`{"sources": [...]}`) or `connect_source` over the API. Polled sources import recent history quietly when connected, then check on a schedule; **Check now** (`poll_source`) checks immediately.

## GitHub

One click creates a private GitHub App for your deployment; install it on the repositories to watch. Pull requests, reviews, check runs, statuses, pushes, commits, and GitHub Actions workflow runs arrive by webhook. The GitHub connector also runs a **readiness poller**: for each open pull request it asks GitHub whether the PR is open, not a draft, mergeable, approved (or needs no review), and green, and records `github.pr.readiness` when that changes. Webhooks nudge it, so readiness follows within seconds.

The last 90 days are imported when repositories are added: pull requests, reviews, check results, completed Actions runs, and commits on the default branch. In a fork, only commits by the account the app is installed on are imported, and a push that syncs the fork records only the pusher's own commits, so upstream history doesn't read as yours. Commits on other branches arrive only live. If commits or runs are missing (a webhook GitHub never delivered), re-import the repository with `coordinate configure '{"backfill": {"repo": "owner/name"}}'`: commits and runs already recorded are skipped. Imported Actions runs need the app's Actions: Read-only permission; `coordinate status` says when a permission is missing.

## Notion

Click **Connect**, pick the pages and teamspaces Coordinate may read on Notion's consent screen, and you're done. (On a deployment without a Notion OAuth app, create an internal integration at notion.so/profile/integrations, paste its secret, and share pages with it instead.) Pages and database rows are recorded as they are created, edited, and archived.

## Cloudflare

Create an API token (not the Global API Key) with read access to Account Settings, Workers Scripts, and Cloudflare Pages. For an account-owned token, add the account ID. Records the account audit log, Worker deploys, and Pages deployment stages.

## Vercel

Paste an access token (add the team ID for a team). A built-in poller records every deployment as it queues, builds, goes live, fails, or is canceled.

## Granola

Paste a `grn_` API key (Business or Enterprise plan). Each meeting's notes arrive when the AI summary is ready.

## Gmail

Click **Connect** and approve read-only access to message headers and labels in Google. Coordinate never reads message bodies. Mail from then on becomes `gmail.message.received` (inbox) and `gmail.message.sent` events with the sender, recipients, subject, snippet, labels, and thread; each thread is a subject (`gmail:thread:<id>`), as is each sender (`email:<address>`). Gmail notifies Coordinate through Google Cloud Pub/Sub within seconds of new mail, and Coordinate also checks every 10 minutes. Disconnecting revokes the grant at Google.

## PostHog

Click **Connect**, sign in to PostHog (US or EU cloud), and pick the project to connect. Coordinate adds an HTTP webhook destination named "Coordinate" to that project, and every event PostHog captures from then on arrives in the log within seconds: `$pageview` as `posthog.pageview`, `Signed Up` as `posthog.signed_up`. The payload has the original `event` name, `distinct_id`, `properties`, the `person` (id and name), and a link to the event in PostHog; each person is a subject (`posthog:person:<distinct_id>`). Coordinate checks the destination every few hours and restores it if it was paused or edited. Disconnecting deletes the destination and revokes the grant. PostHog includes 10,000 destination deliveries a month for free and charges per delivery after that, so a busy project costs money on the PostHog side.

## Linear

Click **Connect** and approve read access in Linear, or paste a personal API key (Settings → Account → Security & access). The last 30 days are imported as history. Afterwards, a one-click connection is updated within seconds: Linear notifies Coordinate of each change, and Coordinate also checks every 15 minutes in case a notification is missed. A pasted key is checked every 2 minutes. Coordinate records issues created, updated, completed, canceled, and archived (`linear.issue.*`) and new comments (`linear.comment.created`). Issue events carry the identifier, title, the first 2,000 characters of the description, state, team, project, cycle, assignee, priority, labels, and due date. Each issue is a subject (`linear:issue:<id>`), as are its team, project, and assignee. Disconnecting a one-click connection revokes the grant at Linear.

## Todoist

Click **Connect** and approve read access in Todoist. Existing tasks are a silent baseline; afterwards tasks added, completed, edited, and deleted, and reminders, become events.

## Web pages and feeds

Give any public https URL and a name. A built-in poller records one event per new entry of an RSS or Atom feed, or an event with the added lines when a page's text changes. Checked hourly by default.

## Claude Code

Connect to get a `settings.json` block that points Claude Code's OpenTelemetry logs at `https://usecoordinate.com/otlp`. Each API call becomes one `claude_code.usage` event with cost and tokens.

An agent can set it up too. Create the source with `coordinate configure '{"sources": [{"kind": "http", "name": "claude-code", "description": "Claude Code telemetry"}]}'`, which returns an ingest token (or, if the source already exists, run `coordinate token claude-code` for a new one). Then merge this into `~/.claude/settings.json` and restart Claude Code:

```json
{"env": {"CLAUDE_CODE_ENABLE_TELEMETRY": "1", "OTEL_LOGS_EXPORTER": "otlp", "OTEL_METRICS_EXPORTER": "none",
  "OTEL_EXPORTER_OTLP_PROTOCOL": "http/json", "OTEL_EXPORTER_OTLP_ENDPOINT": "https://usecoordinate.com/otlp",
  "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer <ingest token>"}}
```

**Connections** recognizes that source and won't create a second one.

## Claude Managed Agents

Add `https://usecoordinate.com/hooks/anthropic/<workspace>/<name>` as a webhook endpoint in the Claude Console and paste its `whsec_` signing secret. Session, agent, deployment, and vault changes arrive as `anthropic.*` events.

## Your apps (HTTP)

Create an HTTP source on **Connections**, or with `coordinate configure '{"sources": [{"kind": "http", "name": "deploys"}]}'`, to get an ingest token that can only append to it. It's shown once; `coordinate token deploys` (the `create_ingest_token` operation) makes another.

```bash
curl -X POST https://usecoordinate.com/v1/sources/deploys/events \
  -H "Authorization: Bearer $INGEST_TOKEN" -H "content-type: application/json" \
  -d '[{"type": "deploy.finished", "producer_event_id": "deploy-481", "payload": {"service": "api", "status": "ok"}}]'
```

Any OpenTelemetry logs exporter can also post OTLP/HTTP JSON to `https://usecoordinate.com/otlp/v1/logs` with an ingest token.

## Event types

### GitHub

| Type | What it records |
| --- | --- |
| `github.pr.readiness` | Emitted when a pull request's readiness may have changed and the result differs from the last one. Payload: repo, number, title, url, author, head_sha, head_short, base_ref, state, draft, mergeable, review_decision, checks, ready, not_ready_reasons, observed_seq. |
| `github.pr.<action>` | Pull request lifecycle passthrough: opened, reopened, edited, synchronize, ready_for_review, converted_to_draft, review_requested, review_request_removed, labeled, merged, closed, and others. Payload: repo, number, title, url, author, head_sha, base_ref, draft, state, action, sender, requested_reviewer, requested_team; opened, reopened, edited, ready_for_review, closed and merged also carry body (the description, up to 16,000 characters). |
| `github.pr.review_<action>` | Review submitted, edited, or dismissed. Payload adds review.state, review.author, review.submitted_at. |
| `github.commit` | One per commit, from pushes (live, with branch) and history imports (default branch). Payload: repo, sha, branch, title, message (up to 16,000 characters), author, author_name, committed_at, url. Keyed by repo and SHA, so a commit is recorded once. |
| `github.check.completed` | A check run finished. Payload: repo, name, status, conclusion, head_sha, head_branch, check_suite_id, run_id (GitHub Actions; joins github.workflow_run.* for the workflow and the event that started it), completed_at, url, app, pull_requests. |
| `github.workflow_run.<action>` | A GitHub Actions workflow run was requested, started (in_progress), or completed. Payload: repo, workflow, run_id, run_number, run_attempt, check_suite_id, status, conclusion, event, branch, head_sha, title, actor, url, started_at, pull_requests. Needs the app's Actions: read permission and the Workflow run event. |

### Notion

| Type | What it records |
| --- | --- |
| `notion.page.created` | A page or database row was created. Payload: page_id, title, url, parent (type, database_id, database), properties (plain values), created_by, created_time. |
| `notion.page.updated` | A page or database row was edited. Notion reports edit times to the minute, so rapid edits collapse into one. Payload as created, plus last_edited_by and last_edited_time. |
| `notion.page.archived` | A page was archived or moved to trash. |
| `notion.database.updated` | A database's title or schema changed, or it was created. |

### Cloudflare

| Type | What it records |
| --- | --- |
| `cloudflare.audit` | A change in the Cloudflare account from its audit log. Payload: action, result, resource_type, resource_id, actor, actor_type, interface, metadata. |
| `cloudflare.worker.deployed` | A Worker deployment. Payload: script, deployment_id, author, source, message, triggered_by (deployment for code, secret for a secret change), versions, from ("audit log" when only the audit log saw it, with no versions). |
| `cloudflare.pages.deployment` | A Pages deployment reached a new stage or status. Payload: project, deployment_id, environment, stage, status, url, branch, commit_hash, commit_message. |

### Granola

| Type | What it records |
| --- | --- |
| `granola.note.created` | Meeting notes are ready (Granola lists a note once its AI summary exists). Payload: note_id, title, web_url, owner, meeting (title, start, end, organiser), attendees (name, email), folders, summary (markdown). |
| `granola.note.updated` | Meeting notes were edited or re-summarized. Payload as created. |

### Todoist

| Type | What it records |
| --- | --- |
| `todoist.task.added` | A task was created. Payload: task_id, content, description, project, due, priority, labels, url. |
| `todoist.task.completed` | A task was checked off. Payload as added, plus completed_at. |
| `todoist.task.updated` | A task's text, due date, priority, labels, or project changed. |
| `todoist.task.deleted` | A task was deleted. |
| `todoist.reminder.added` | A reminder alert was set on a task. Payload: reminder_id, task_id, task, type (absolute, relative, location), remind_at (local time, when known), minute_offset (relative: minutes before the task is due), location, url. |
| `todoist.reminder.updated` | A reminder's time or offset changed. Payload as added. |
| `todoist.reminder.deleted` | A reminder was removed (Todoist also removes reminders once they fire). Payload as added. |
| `todoist.reminder.fired` | A reminder went off (one-click connections only; sent by Todoist's webhook). Payload as added. |

### Gmail

| Type | What it records |
| --- | --- |
| `gmail.message.received` | A message arrived (inbox). Payload: message_id, thread_id, account, from, from_address, to, cc, subject, snippet, labels, unread, list_id, url. Headers and snippet only, never the body. |
| `gmail.message.sent` | The account sent a message. Payload as received. |

### PostHog

| Type | What it records |
| --- | --- |
| `posthog.<event>` | Every event captured in the PostHog project, as it is ingested: posthog.pageview for $pageview, posthog.signed_up for "Signed Up". Payload: event (the original name), distinct_id, properties, person {id, name, url}, url (the event in PostHog). |

### Linear

| Type | What it records |
| --- | --- |
| `linear.issue.created` | An issue was created. Payload: issue_id, identifier (ENG-123), title, description (first 2,000 characters), url, state, state_type (backlog, unstarted, started, completed, canceled, triage), team (key), project, cycle, assignee, creator, priority, estimate, due, labels, parent, created_at, updated_at, completed_at, canceled_at, archived_at. |
| `linear.issue.updated` | An issue's title, description, state, assignee, priority, estimate, due date, labels, project, cycle, or parent changed. Payload as created. |
| `linear.issue.completed` | An issue moved to a completed state. Payload as created. |
| `linear.issue.canceled` | An issue moved to a canceled state. Payload as created. |
| `linear.issue.archived` | An issue was archived. Payload as created. |
| `linear.comment.created` | Someone commented on an issue. Payload: comment_id, body (first 2,000 characters), url, author, issue (id, identifier, title, team), created_at. |

### Vercel (poller)

| Type | What it records |
| --- | --- |
| `vercel.deployment.<state>` | A deployment reached a state: queued, initializing, building, ready, error, or canceled. Payload: project, deployment_id, url, state, target, creator, branch, commit_sha, commit_message, commit_author, inspector_url. |

### Web pages and feeds (poller)

| Type | What it records |
| --- | --- |
| `web.feed.item` | A new entry in an RSS or Atom feed. Payload: id, title, link, published, summary, feed, url. |
| `web.page.changed` | The text of a watched page changed. Payload: url, title, added_lines (up to 40), added_count, removed_count, excerpt. |

### Claude Code

| Type | What it records |
| --- | --- |
| `claude_code.usage` | One Claude Code API call: model, cost_usd, cost_microusd (integer), input/output/cache tokens, tokens_total, day (UTC), hour, session_id, user. Derived from the api_request/api_response records. |
| `claude_code.user_prompt` | A prompt was submitted (text only when OTEL_LOG_USER_PROMPTS=1). |
| `claude_code.api_error` | An API request failed: status_code, error. |
| `claude_code.api_refusal` | The model declined a request. |

### Claude Managed Agents

| Type | What it records |
| --- | --- |
| `anthropic.<resource>.<event>` | One per webhook delivery, named after the Console event (for example anthropic.session.status_idle). Payload: resource_type, resource_id, organization_id, workspace_id, event_id. |

### Coordinate (source coordinate)

| Type | What it records |
| --- | --- |
| `trigger.fired` | A trigger fired. Payload: trigger, trigger_id, firing_id, reason, key (query triggers with key), event_id and event_type (event triggers), rows (query triggers). |
| `action.succeeded` | An action of a firing was accepted. Payload: trigger, firing_id, action (index), kind, status_code, recorded (fields kept from the response). |
| `action.failed` | An action failed after its retries, or for good. Payload: trigger, firing_id, action, kind, status_code, error, attempts. |
| `login.*` | Sign-ins: login.signed_in, login.requested, login.approved, login.denied. Payload includes a one-line summary. |
