Export Your Data
Download your tracker data — a single dataset, a whole project as one ZIP, or your whole organization as a background job.
Overview
UluOps lets you take your data with you. There are three export shapes, each a paid-tier capability on the tracker API:
| Shape | Endpoint | Scope | Delivery |
|---|---|---|---|
| Single dataset | GET /export/projects/:project/:dataset | One dataset of one project | Streamed file (NDJSON or CSV) |
| Project bundle | GET /export/projects/:project/bundle | All 8 datasets of one project | Streamed ZIP |
| Org export job | POST /export/jobs → download | Every project in an organization | Background job → ZIP download |
The eight datasets are issues, issue-notes, status-history, occurrences,
runs, agent-snapshots, analysis-summaries, and analysis-records. Each
carries the foreign keys needed to rejoin them (issues.id/fingerprint,
runs.id, occurrences.issueId+runId, …).
Formats. NDJSON (default) is lossless — JSON columns nest natively. CSV is a spreadsheet convenience (RFC 4180, UTF-8 BOM, CRLF, formula-injection neutralized); Excel coerces dates/long numbers and truncates very large cells.
Who can export
Single-dataset export needs a paid tier (any non-free subscription) — you can export data you can already read.
The two bulk shapes (project bundle and org job) additionally require a
publisher-or-above org role and are closed to system/API-automation keys.
Bulk export vacuums a whole project or organization into a portable archive, so
it is gated more tightly than paginated reading. Personal-organization members
are owners, so exporting your own personal org is never blocked.
Single dataset
Stream one dataset as a file:
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
"https://api.uluops.ai/api/v1/export/projects/$PROJECT/issues?format=ndjson" \
-o issues.ndjsonFilters vary by dataset (for example status, priority, severity,
include_deleted on issues). The as_of query parameter is an insertion
watermark — rows created after it are excluded — so passing the same explicit
as_of to several calls gives you a coherent multi-dataset cut.
If the result exceeds the synchronous row cap you get 413 EXPORT_TOO_LARGE;
its details.asyncEndpoint points you at the org export job.
Project bundle (one ZIP, all datasets)
One request, one archive, one coherent snapshot:
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
"https://api.uluops.ai/api/v1/export/projects/$PROJECT/bundle?format=ndjson" \
-o project-bundle.zipThe ZIP holds one entry per dataset ({dataset}.{ext}) plus a top-level
manifest.json:
{
"schemaVersion": 1,
"asOf": "2026-07-08T00:00:00.000Z",
"coherence": "insert-level",
"project": { "id": "…", "name": "…" },
"rowCounts": { "issues": 128, "runs": 12, "…": 0 }
}All eight datasets share one as_of, so cross-dataset joins are
insert-coherent — every row present was created at or before that watermark.
(Later mutations like status changes are read live during the build; the
snapshot bounds row creation, not every field.)
A bundle streams straight through the connection. If it is interrupted, the ZIP
arrives without its central directory and simply fails to open — that is the
signal to retry, and nothing partial is treated as complete. If the project is
too large for a synchronous bundle you get 413 EXPORT_TOO_LARGE (or 504 EXPORT_PREPARE_TIMEOUT) — use the org export job instead.
Org export job (background, whole organization)
For a whole organization — or any project past the synchronous caps — queue an asynchronous job. The job builds a ZIP of every project in the org in the background; you download it when it is ready.
1. Queue it. The body is optional (format, as_of, and the four
visibility flags):
curl -X POST -H "Authorization: Bearer $ULUOPS_API_KEY" \
"https://api.uluops.ai/api/v1/export/jobs"You get back a job. A 201 means a new job was queued; a 200 means your org
already has an active job (there is one active job per org at a time, and you
are limited to a few new jobs per hour).
{ "job": { "id": "…", "status": "queued", "asOf": "…", "createdAt": "…", "requestedBy": "…" } }The archive is a snapshot as of when you queued it, not as of when it
finishes — the asOf field makes that visible.
2. Watch it. Poll the job (status moves queued → running → ready), or
list your org's jobs, or just wait for the "your export is ready" email. The
email links to your dashboard — never a direct file link — because artifacts are
downloadable only by signed-in members of your organization.
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
"https://api.uluops.ai/api/v1/export/jobs/$JOB_ID"3. Download it. Once ready:
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
"https://api.uluops.ai/api/v1/export/jobs/$JOB_ID/download" \
-o org-export.zipInside, each project is its own directory named
{sanitizedProjectName}__{first8OfProjectId} (the ID suffix keeps two
similarly-named projects distinct), with the eight dataset files under it. The
manifest.json maps each directory back to its project and carries per-project,
per-dataset rowCounts.
Lifecycle notes:
- Jobs are kept for 72 hours after they are ready, then swept (
expired); a download after that returns410. - You can cancel a job while it is still
queued; a running build can't be canceled (409). - A
failedjob shows anerrorCodein its status; re-running is one call.
The job endpoints return resource-keyed bodies — { "job": … } and
{ "jobs": [ … ] } — rather than the { "data": … } envelope used elsewhere in
the tracker API.
From the dashboard
Everything above is also on the dashboard's Data Export page
(/settings/export): pick an organization and project for a single-dataset
download or the "Export all datasets (ZIP)" bundle, and use the org jobs panel
to queue a whole-org export, watch its status, and download it when ready. The
dashboard verifies bundle integrity on your behalf and warns you if a download
was truncated.