RegistryDashboard

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:

ShapeEndpointScopeDelivery
Single datasetGET /export/projects/:project/:datasetOne dataset of one projectStreamed file (NDJSON or CSV)
Project bundleGET /export/projects/:project/bundleAll 8 datasets of one projectStreamed ZIP
Org export jobPOST /export/jobs → downloadEvery project in an organizationBackground 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:

bash
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
  "https://api.uluops.ai/api/v1/export/projects/$PROJECT/issues?format=ndjson" \
  -o issues.ndjson

Filters 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:

bash
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
  "https://api.uluops.ai/api/v1/export/projects/$PROJECT/bundle?format=ndjson" \
  -o project-bundle.zip

The ZIP holds one entry per dataset ({dataset}.{ext}) plus a top-level manifest.json:

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):

bash
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).

json
{ "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.

bash
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
  "https://api.uluops.ai/api/v1/export/jobs/$JOB_ID"

3. Download it. Once ready:

bash
curl -H "Authorization: Bearer $ULUOPS_API_KEY" \
  "https://api.uluops.ai/api/v1/export/jobs/$JOB_ID/download" \
  -o org-export.zip

Inside, 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 returns 410.
  • You can cancel a job while it is still queued; a running build can't be canceled (409).
  • A failed job shows an errorCode in its status; re-running is one call.
Note

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.