Tracking issue for adding an admin-only cross-account aggregate mode to the task-runs endpoint. Today the "recent task runs" tab is per-account only; there is no way for an admin to see the latest runs across all accounts in one call. Feasibility is already proven against the live Trigger.dev API (see Context).
Goal
An admin can fetch the latest task runs across all accounts from GET /api/tasks/runs, with each run attributed to its owning account — instead of being limited to a single account_id. The per-account behavior the UI already uses stays unchanged.
Touch points (all in api):
api/lib/trigger/fetchTriggerRuns.ts — Trigger.dev fetch + TriggerRunFilter type
api/lib/tasks/validateGetTaskRunQuery.ts — auth + query validation
api/lib/tasks/getTaskRunHandler.ts — list handler
Consumer for reference (out of scope here): chat/lib/tasks/getTaskRuns.ts → chat/hooks/useTaskRuns.ts → chat/components/TasksPage/*.
PRs (updated 2026-06-29)
| PR |
Item |
Base |
State |
| TBD |
fetchTriggerRuns: allow no-filter (all-accounts) fetch |
test |
⏳ not opened |
| TBD |
validateGetTaskRunQuery: admin-only scope=all list mode |
test |
⏳ not opened |
| TBD |
getTaskRunHandler: aggregate fetch + per-run account attribution |
test |
⏳ not opened |
No PRs opened yet. The three items are interdependent and only end-to-end testable together, so they may ship as one api PR (base test) rather than three; kept as separate line items for review clarity. Merge path: api PRs target test, then test→main.
Context — feasibility verified
Task runs are Trigger.dev runs (the tasks flow), each tagged account:<id>. The current endpoint hard-scopes to one account via filter[tag]=account:<id>. Calling the same Trigger.dev endpoint without the tag filter (GET https://api.trigger.dev/api/v1/runs?page[size]=N) returns runs across all accounts, each still carrying its account:<id> tag for attribution.
Verified live on 2026-06-29: 137 task runs that day across 49 distinct accounts — 135 customer-prompt-task, 1 send-pulses-task, 1 pro-artist-social-profiles-scrape (136 COMPLETED / 1 QUEUED). Morning batch concentration: 09:00 UTC → 56, 10:00 UTC → 51. Trigger.dev caps page[size] at 100 and paginates by cursor (pagination.next → page[after]), so a full-day sweep loops the cursor.
Open — admin aggregate mode (3 items; in implement order)
Out of scope
- Chat UI consumer (an admin "all accounts" view in
TasksPage) — separate follow-up. These three items are the api enablement only.
- Joining runs to their delivered emails (run → email value view) — possible future follow-up.
Architecture decisions
- Task runs live in Trigger.dev, tagged
account:<id>. The /api/tasks/runs list endpoint is a thin proxy over Trigger.dev's GET /api/v1/runs (TRIGGER_SECRET_KEY, set in Vercel). Account attribution rides on the run tag, so aggregate mode reads the tag back rather than needing a new store.
- Aggregate is strictly admin-gated. Returning all accounts' runs is sensitive; reuse the existing
checkIsAdmin check — no new auth surface.
Source references
- Endpoint chain:
chat/lib/tasks/getTaskRuns.ts (client, GET /api/tasks/runs?account_id=&limit=) → api/app/api/tasks/runs/route.ts → api/lib/tasks/getTaskRunHandler.ts → api/lib/trigger/fetchTriggerRuns.ts.
- Trigger.dev Management API:
GET https://api.trigger.dev/api/v1/runs (filters: filter[tag], filter[schedule]; page[size] ≤ 100; cursor page[after]).
Tracking issue for adding an admin-only cross-account aggregate mode to the task-runs endpoint. Today the "recent task runs" tab is per-account only; there is no way for an admin to see the latest runs across all accounts in one call. Feasibility is already proven against the live Trigger.dev API (see Context).
Goal
An admin can fetch the latest task runs across all accounts from
GET /api/tasks/runs, with each run attributed to its owning account — instead of being limited to a singleaccount_id. The per-account behavior the UI already uses stays unchanged.Touch points (all in
api):api/lib/trigger/fetchTriggerRuns.ts— Trigger.dev fetch +TriggerRunFiltertypeapi/lib/tasks/validateGetTaskRunQuery.ts— auth + query validationapi/lib/tasks/getTaskRunHandler.ts— list handlerConsumer for reference (out of scope here):
chat/lib/tasks/getTaskRuns.ts→chat/hooks/useTaskRuns.ts→chat/components/TasksPage/*.PRs (updated 2026-06-29)
testscope=alllist modetesttestContext — feasibility verified
Task runs are Trigger.dev runs (the
tasksflow), each taggedaccount:<id>. The current endpoint hard-scopes to one account viafilter[tag]=account:<id>. Calling the same Trigger.dev endpoint without the tag filter (GET https://api.trigger.dev/api/v1/runs?page[size]=N) returns runs across all accounts, each still carrying itsaccount:<id>tag for attribution.Verified live on 2026-06-29: 137 task runs that day across 49 distinct accounts — 135
customer-prompt-task, 1send-pulses-task, 1pro-artist-social-profiles-scrape(136 COMPLETED / 1 QUEUED). Morning batch concentration: 09:00 UTC → 56, 10:00 UTC → 51. Trigger.dev capspage[size]at 100 and paginates by cursor (pagination.next→page[after]), so a full-day sweep loops the cursor.Open — admin aggregate mode (3 items; in implement order)
fetchTriggerRuns: support a no-filter (all-accounts) fetch.TriggerRunFilteris typed{ "filter[tag]": string } | { "filter[schedule]": string }and the function always sets afilter[*]param, so there is no path to query runs across all accounts. (source:api/lib/trigger/fetchTriggerRuns.ts){}/ make the filter optional); when empty, build the request with nofilter[tag]/filter[schedule]param. Preservepage[size]and cursor pagination (page[after]).fetchTriggerRuns({}, 20)issues a request with nofilter[*]param and returns the latest 20 runs across all accounts — unit test asserts the URL omits the filter param.validateGetTaskRunQuery: add an admin-onlyscope=alllist mode.accountId; non-admins must stay scoped to their own/org accounts. There is no sanctioned way to request all accounts. (source:api/lib/tasks/validateGetTaskRunQuery.ts)scope=all) that, only whencheckIsAdmin(authResult.accountId)is true, returns a new{ mode: "list-all", limit }. A non-admin passing it gets403. Existingaccount_id=<id>and self/org paths unchanged.?scope=all→ resolves tolist-all; non-admin +?scope=all→403; normal per-account requests behave exactly as before — covered by tests.getTaskRunHandler: aggregate fetch + per-run account attribution.api/lib/tasks/getTaskRunHandler.ts)list-allmode callfetchTriggerRuns({}, limit); parse each run'saccount:<id>tag into an explicit field (e.g.accountId) so the response attributes every run to an account. Return{ status: "success", runs }.GET /api/tasks/runs?scope=all&limit=20returns the latest 20 runs across accounts, each with itsaccountId— preview result matches Trigger.dev ground truth (e.g. 2026-06-29: 137 runs / 49 accounts).Out of scope
TasksPage) — separate follow-up. These three items are theapienablement only.Architecture decisions
account:<id>. The/api/tasks/runslist endpoint is a thin proxy over Trigger.dev'sGET /api/v1/runs(TRIGGER_SECRET_KEY, set in Vercel). Account attribution rides on the run tag, so aggregate mode reads the tag back rather than needing a new store.checkIsAdmincheck — no new auth surface.Source references
chat/lib/tasks/getTaskRuns.ts(client,GET /api/tasks/runs?account_id=&limit=) →api/app/api/tasks/runs/route.ts→api/lib/tasks/getTaskRunHandler.ts→api/lib/trigger/fetchTriggerRuns.ts.GET https://api.trigger.dev/api/v1/runs(filters:filter[tag],filter[schedule];page[size]≤ 100; cursorpage[after]).