Export Center REST API

Audience: Platform integrators, Console/CLI developers, and automation engineers orchestrating export runs.
Base route: /api/export/* behind the StellaOps gateway; requires Authority-issued tokens with export scopes.

This reference describes the Export Center API introduced in Export Center Phase 1 (Epic 10) and extended in Phase 2. Use it alongside the Export Center Architecture and Profiles guides for service-level semantics.

Status: Endpoint implementation lands with EXPORT-SVC-35-006 (Sprint 35) and related follow-on tasks. As of the current build the WebService hosts only the template stub; use this contract for coordination and update once the API is wired.

1. Authentication and headers

  • Authorization: Bearer tokens in Authorization: Bearer <token> paired with DPoP proof. Required scopes per endpoint:
    • export:profile:manage for profile CRUD.
    • export:run to submit and cancel runs.
    • export:read to list and inspect runs.
    • export:download for bundle downloads and manifests.
  • Tenant context: Provide X-Stella-Tenant when the token carries multiple tenants; defaults to token tenant otherwise.
  • Idempotency: Mutating endpoints accept Idempotency-Key (UUID). Retrying with the same key returns the original result.
  • Rate limits and quotas: Responses include X-Stella-Quota-Limit, X-Stella-Quota-Remaining, and X-Stella-Quota-Reset. Exceeding quotas returns 429 Too Many Requests with ERR_EXPORT_QUOTA.
  • Content negotiation: Requests and responses use application/json; charset=utf-8 unless otherwise stated. Downloads stream binary content with profile-specific media types.
  • SSE: Event streams set Content-Type: text/event-stream and keep connections alive with comment heartbeats every 15 seconds.

2. Error model

Errors follow standard HTTP codes with structured payloads:

{
  "code": "ERR_EXPORT_002",
  "message": "Profile not found for tenant acme",
  "details": [],
  "traceId": "01J9N4Y4K2XY8C5V7T2S",
  "timestamp": "2025-10-29T13:42:11Z"
}
CodeDescriptionTypical HTTP statusNotes
ERR_EXPORT_001Validation failure (selectors, configuration)400details enumerates offending fields.
ERR_EXPORT_002Profile missing or not accessible for tenant404Returned on run submission or profile fetch.
ERR_EXPORT_003Concurrency or quota exceeded429Includes retryAfterSeconds in details.
ERR_EXPORT_004Adapter failure (schema mismatch, upstream outage)502Worker logs contain adapter error reason.
ERR_EXPORT_005Signing or KMS error500Run marked failed with errorCode=signing.
ERR_EXPORT_006Distribution failure (HTTP, OCI, object storage)502details lists failing distribution driver.
ERR_EXPORT_007Run canceled or expired409Includes cancel author and timestamp.
ERR_EXPORT_BASE_MISSINGBase manifest for delta exports not found400Specific to mirror:delta.
ERR_EXPORT_EMPTYNo records matched selectors (when allowEmpty=false)422Useful for guard-railled automation.
ERR_EXPORT_QUOTADaily quota exhausted429Always paired with quota headers.

All responses include traceId for correlation with logs and metrics.

3. Profiles endpoints

3.1 List profiles

GET /api/export/profiles?kind=json&variant=raw&page=1&pageSize=20
Scopes: export:read

Returns tenant-scoped profiles. Response headers: X-Total-Count, Link for pagination.

Response

{
  "items": [
    {
      "profileId": "prof-json-raw",
      "name": "Daily JSON Raw",
      "kind": "json",
      "variant": "raw",
      "distribution": ["http", "object"],
      "retention": {"mode": "days", "value": 14},
      "createdAt": "2025-10-23T08:00:00Z",
      "createdBy": "user:ops"
    }
  ],
  "page": 1,
  "pageSize": 20
}

3.2 Get a profile

GET /api/export/profiles/{profileId}
Scopes: export:read

Returns full configuration, including config payload, distribution options, and metadata.

3.3 Create a profile

POST /api/export/profiles
Scopes: export:profile:manage

Request

{
  "profileId": "prof-airgap-mirror",
  "name": "Airgap Mirror Weekly",
  "kind": "mirror",
  "variant": "full",
  "include": ["advisories", "vex", "sboms", "policy"],
  "distribution": ["http", "object"],
  "encryption": {
    "enabled": true,
    "recipientKeys": ["age1tenantkey..."],
    "strict": false
  },
  "retention": {"mode": "days", "value": 30}
}

Response 201

{
  "profileId": "prof-airgap-mirror",
  "version": 1,
  "createdAt": "2025-10-29T12:05:22Z",
  "createdBy": "user:ops",
  "status": "active"
}

3.4 Update profile metadata

PATCH /api/export/profiles/{profileId}
Scopes: export:profile:manage

Allows renaming, toggling distribution switches, or updating retention. Structural configuration updates (kind/variant/include) create a new revision; the API returns revisionCreated=true and the new profileId (e.g., prof-airgap-mirror@2).

3.5 Archive profile

POST /api/export/profiles/{profileId}:archive
Scopes: export:profile:manage

Marks profile as inactive; existing runs remain accessible. Use :restore to reactivate.

4. Run management

4.1 Submit an export run

POST /api/export/runs
Scopes: export:run

Request

{
  "profileId": "prof-json-raw",
  "selectors": {
    "tenants": ["acme"],
    "timeWindow": {
      "from": "2025-10-01T00:00:00Z",
      "to": "2025-10-29T00:00:00Z"
    },
    "products": ["registry.example.com/app:*"],
    "sboms": ["sbom:S-1001", "sbom:S-2004"]
  },
  "policySnapshotId": "policy-snap-42",
  "options": {
    "allowEmpty": false,
    "priority": "standard"
  }
}

Response 202

{
  "runId": "run-20251029-01",
  "status": "pending",
  "profileId": "prof-json-raw",
  "createdAt": "2025-10-29T12:12:11Z",
  "createdBy": "user:ops",
  "selectors": { "...": "..." },
  "links": {
    "self": "/api/export/runs/run-20251029-01",
    "events": "/api/export/runs/run-20251029-01/events"
  }
}

4.2 List runs

GET /api/export/runs?status=active&profileId=prof-json-raw&page=1&pageSize=10
Scopes: export:read

Returns latest runs with pagination. Each item includes summary counts, duration, and last event.

4.3 Get run status

GET /api/export/runs/{runId}
Scopes: export:read

Response fields:

FieldDescription
statuspending, running, success, failed, canceled.
progressObject with adapters, bytesWritten, recordsProcessed.
errorCodePopulated when status=failed (signing, distribution, etc).
policySnapshotIdReturned for policy-aware profiles.
distributionsList of available distribution descriptors (type, location, sha256, expiresAt).

4.4 Cancel a run

POST /api/export/runs/{runId}:cancel
Scopes: export:run

Body optional ({"reason": "Aborted due to incident INC-123"}). Returns 202 and pushes run.canceled event.

5. Events and telemetry

5.1 Server-sent events

GET /api/export/runs/{runId}/events
Scopes: export:read
Accept: text/event-stream

Event payload example:

event: run.progress
data: {"runId":"run-20251029-01","phase":"adapter","adapter":"json","records":1024,"bytes":7340032,"timestamp":"2025-10-29T12:13:15Z"}

Event types:

EventMeaning
run.acceptedPlanner accepted job and queued with Orchestrator.
run.progressPeriodic updates with phase, adapter, counts.
run.distributionDistribution driver finished (includes descriptor).
run.signedSigning completed successfully.
run.succeededRun marked success.
run.failedRun failed; payload includes errorCode.
run.canceledRun canceled; includes canceledBy.

SSE heartbeats (: ping) keep long-lived connections alive and should be ignored by clients.

5.2 Audit events

GET /api/export/runs/{runId}/events?format=audit returns the same event stream in newline-delimited JSON for offline ingestion.

6. Download endpoints

6.1 Bundle download

GET /api/export/runs/{runId}/download
Scopes: export:download

Streams the primary bundle (tarball, zip, or profile-specific layout). Headers:

  • Content-Disposition: attachment; filename="export-run-20251029-01.tar.zst"
  • X-Export-Digest: sha256:...
  • X-Export-Size: 73482019
  • X-Export-Encryption: age (when mirror encryption enabled)

Supports HTTP range requests for resume functionality. If no bundle exists yet, responds 409 with ERR_EXPORT_007.

6.2 Manifest download

GET /api/export/runs/{runId}/manifest
Scopes: export:download

Returns signed export.json. To fetch the detached signature, append ?signature=true.

6.3 Provenance download

GET /api/export/runs/{runId}/provenance
Scopes: export:download

Returns signed provenance.json. Supports ?signature=true. Provenance includes attestation subject digests, policy snapshot ids, adapter versions, and KMS key identifiers.

6.4 Distribution descriptors

GET /api/export/runs/{runId}/distributions
Scopes: export:read

Lists all registered distribution targets (HTTP, OCI, object storage). Each item includes type, location, sha256, sizeBytes, and expiresAt.

7. Webhook hand-off

Exports can notify external systems once a run succeeds by registering an HTTP webhook:

POST /api/export/webhooks
Scopes: export:profile:manage

Payload includes targetUrl, events (e.g., run.succeeded), and optional secret for HMAC signatures. Webhook deliveries sign payloads with X-Stella-Signature header (sha256=...). Retries follow exponential backoff with dead-letter capture in export_events.

8. Observability

  • Metrics endpoint: /metrics (service-local) exposes Prometheus metrics listed in Architecture.
  • Tracing: When traceparent header is provided, worker spans join the calling trace.
  • Run lookup by trace: Use GET /api/export/runs?traceId={id} when troubleshooting distributed traces.

Imposed rule: Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.