11 KiB
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:managefor profile CRUD.export:runto submit and cancel runs.export:readto list and inspect runs.export:downloadfor bundle downloads and manifests.
- Tenant context: Provide
X-Stella-Tenantwhen 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, andX-Stella-Quota-Reset. Exceeding quotas returns429 Too Many RequestswithERR_EXPORT_QUOTA. - Content negotiation: Requests and responses use
application/json; charset=utf-8unless otherwise stated. Downloads stream binary content with profile-specific media types. - SSE: Event streams set
Content-Type: text/event-streamand 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"
}
| Code | Description | Typical HTTP status | Notes |
|---|---|---|---|
ERR_EXPORT_001 |
Validation failure (selectors, configuration) | 400 | details enumerates offending fields. |
ERR_EXPORT_002 |
Profile missing or not accessible for tenant | 404 | Returned on run submission or profile fetch. |
ERR_EXPORT_003 |
Concurrency or quota exceeded | 429 | Includes retryAfterSeconds in details. |
ERR_EXPORT_004 |
Adapter failure (schema mismatch, upstream outage) | 502 | Worker logs contain adapter error reason. |
ERR_EXPORT_005 |
Signing or KMS error | 500 | Run marked failed with errorCode=signing. |
ERR_EXPORT_006 |
Distribution failure (HTTP, OCI, object storage) | 502 | details lists failing distribution driver. |
ERR_EXPORT_007 |
Run canceled or expired | 409 | Includes cancel author and timestamp. |
ERR_EXPORT_BASE_MISSING |
Base manifest for delta exports not found | 400 | Specific to mirror:delta. |
ERR_EXPORT_EMPTY |
No records matched selectors (when allowEmpty=false) |
422 | Useful for guard-railled automation. |
ERR_EXPORT_QUOTA |
Daily quota exhausted | 429 | Always 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:
| Field | Description |
|---|---|
status |
pending, running, success, failed, canceled. |
progress |
Object with adapters, bytesWritten, recordsProcessed. |
errorCode |
Populated when status=failed (signing, distribution, etc). |
policySnapshotId |
Returned for policy-aware profiles. |
distributions |
List 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:
| Event | Meaning |
|---|---|
run.accepted |
Planner accepted job and queued with Orchestrator. |
run.progress |
Periodic updates with phase, adapter, counts. |
run.distribution |
Distribution driver finished (includes descriptor). |
run.signed |
Signing completed successfully. |
run.succeeded |
Run marked success. |
run.failed |
Run failed; payload includes errorCode. |
run.canceled |
Run 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: 73482019X-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
traceparentheader is provided, worker spans join the calling trace. - Run lookup by trace: Use
GET /api/export/runs?traceId={id}when troubleshooting distributed traces.
9. Related documentation
- Export Center Overview
- Export Center Architecture
- Export Center Profiles
- Export Center CLI Guide (companion document)
- Aggregation-Only Contract reference
Imposed rule: Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.