Install the Devin poller
Devin runs in Cognition’s cloud, so there’s nothing to install inside it. Instead, the Cardinal Devin poller runs on infrastructure you control, reads your organization’s Devin sessions through the Devin API, and sends them to the Agent Outcomes dashboard. Run one poller per Devin organization.
Reach out to support@cardinalhq.io for support or to ask questions not answered in our documentation.
What it captures
- Sessions and pull requests — each Devin session that opens a pull request is attributed to its repo and PR. With a GitHub token, the poller also resolves the PR’s head branch, commit, and initiative.
- ACU usage — Devin bills in ACUs (Agent Compute Units) rather than tokens, and the poller reports them alongside sessions so Devin spend shows up next to Claude and Codex token spend on the dashboard. See ACU usage capture below.
- Decisions (opt-in) — sessions started with Cardinal’s decision playbook and output schema report the choices Devin made. They’re recorded as they appear in the session’s structured output.
It does not capture per-message activity or tool calls. Sessions that don’t open a pull request aren’t shown.
Requirements
- A Devin service user API key (created by a Devin org admin). Set
DEVIN_ORG_IDtoo if you use Devin’s v3 API. - A Cardinal account on
app.cardinalhq.io, or a self-hosted Cardinal UI. Your Cardinal deployment must be recent enough to accept Devin sessions; on self-hosted installs, ask your operator. - Optional: a GitHub token with read access to the repos Devin works in, for branch and initiative attribution.
- To run it: Docker, Kubernetes, or Python 3.9+ on macOS or Linux.
1. Connect to Cardinal (container)
Create a volume for the poller’s state, then run connect once. It prints a URL like https://app.cardinalhq.io/connect?code=ABCD-EFGH; open it, sign in, pick your organization, and click Approve. The credential is saved in the volume.
docker volume create cardinal-devin-data
docker run -it --rm -v cardinal-devin-data:/data \
ghcr.io/cardinalhq/cardinal-devin-poller:v0.2.0 connectFor a self-hosted Cardinal UI, add --host https://ui.example.internal after connect.
2. Run the poller
docker run -d --name cardinal-devin-poller --restart unless-stopped \
-e DEVIN_API_KEY -e DEVIN_ORG_ID -e GITHUB_TOKEN \
-v cardinal-devin-data:/data \
ghcr.io/cardinalhq/cardinal-devin-poller:v0.2.0The poller checks Devin every five minutes and keeps its progress in /data, so restarts don’t send sessions twice. Keep one poller per organization, and never let two pollers share a state volume.
Without the connect step (for example on Kubernetes), pass the Cardinal ingest endpoint and key as environment variables instead: CARDINAL_INGEST_ENDPOINT and CARDINAL_INGEST_API_KEY (both required together), plus optional CARDINAL_ORG and CARDINAL_DEPLOYMENT_ENV. Environment values take precedence over a stored connection. The poller README has a Kubernetes example: run a single replica with a persistent volume for /data.
3. Verify
docker run --rm -v cardinal-devin-data:/data \
-e DEVIN_API_KEY -e DEVIN_ORG_ID -e GITHUB_TOKEN \
ghcr.io/cardinalhq/cardinal-devin-poller:v0.2.0 statusStatus shows where the Cardinal credential comes from (never the key itself), whether a Devin key and GitHub token are set, and the poller’s progress. To see what would be sent without sending it, run poll --once --dry-run the same way. Devin sessions with pull requests appear on the Outcomes dashboard within a few poll cycles.
ACU usage capture
Devin bills in ACUs (Agent Compute Units) rather than tokens, so the poller reads consumption from the Devin API and reports it alongside every session. On the Outcomes dashboard, Devin spend then sits next to Claude and Codex token spend in the KPI strip, the ledger, and the per-initiative and per-PR panels.
For each session, the poller emits a cardinal.turn_usage record with:
cardinal_billing_unit = "acu"— flags this as ACU spend, not tokens.cardinal_acu_total— the session’s total ACUs, summed across the days it ran.cardinal_acu_cascade,cardinal_acu_devin,cardinal_acu_review,cardinal_acu_terminal— the per-product ACU split, on v3 tenants only (see below).period_start_ns/period_end_ns— the session’s active window.
Each usage record carries the same repo, branch, PR, and initiative attribution as the session’s cardinal.git_state and cardinal.decision events, so it lines up with the same case files.
Required Devin key scope
Usage capture calls the Devin consumption endpoint, which is a scoped capability on service-user keys. When you create the service user, tick the consumption scope alongside the session-read scope the rest of the poller needs.
If the key doesn’t have consumption scope, the poller logs a one-time warning and turns usage off for that key. Sessions, pull requests, and decisions keep flowing — only the ACU numbers are missing. To fix it, grant the scope to the existing key (or issue a new one) and restart the poller.
v1 vs v3 tenants
The poller uses whichever Devin API version your key targets:
- v1 keys — Devin returns a single enterprise-wide consumption figure for the poll window. The poller attributes that total across the sessions in the window; the per-product columns (
cardinal_acu_cascadeand friends) are not available. - v3 keys — Devin returns per-session, per-day consumption with the product-level split.
cardinal_acu_totalis that session’s own ACUs, and the per-product columns are populated.
Set DEVIN_ORG_ID on the poller for v3 keys, as the Requirements section notes. Nothing else in the install steps changes.
Decision capture
Decisions only come from sessions created with Cardinal’s playbook and output schema, both shipped with the poller. The playbook tells Devin to record material choices, and the schema makes Devin return them in the session’s structured output:
- Create a Devin playbook from the playbook text in the poller README , and note its id.
- When you create a Devin session through the API, pass that id as
playbook_idand the schema asstructured_output_schema.
Print the schema with:
docker run --rm ghcr.io/cardinalhq/cardinal-devin-poller:v0.2.0 decision-schemaThe playbook is in the poller README . Each decision is attached to the session’s repo, branch, and pull request.
Without Docker
Download the release and run the poller directly (Python 3.9+):
VERSION=0.2.0
gh release download "devin-v${VERSION}" --repo cardinalhq/cardinal-agent-plugins \
--pattern "cardinal-devin-${VERSION}.tar.gz"
tar -xzf "cardinal-devin-${VERSION}.tar.gz"
"./cardinal-devin-${VERSION}/bin/cardinal-devin" connect
"./cardinal-devin-${VERSION}/bin/cardinal-devin" poll --interval 300State is kept in ~/.cardinal-devin unless you set CARDINAL_DEVIN_HOME.
Limits
- Polling means sessions appear minutes after they update, not instantly.
- If the poller is down longer than its lookback window (seven days by default), sessions that finished during the gap are skipped.
- Attribution uses the email Devin reports for the session’s user; sessions whose user can’t be resolved show as
unknown.
Need help
See the poller README for every option, or contact support.