// Platform

Data Export

Walk away with everything stored. Two surfaces: a per-project Data Export API for logs, metrics, usage, and execution audit, and a GDPR surface for personal-data export and account deletion. Downloads are NDJSON, the lifecycle is queue, poll, download, and the deletion path holds a grace window before the data goes.

Max export window
90 daysWider ranges rejected with 400.
Download format
application/x-ndjsonAlways, regardless of requested format.
GDPR download window
30 daysPer-account cadence also 30 days.
Deletion grace
30 daysCancellable until the grace window expires.

Two export systems:

  • Data Export API (/exports/), Export request logs, aggregated metrics, and usage/billing data in CSV, JSON, or JSONL format.
  • GDPR Data Rights (/gdpr/), Export all personal data or request account deletion with a configurable grace period.

Data Export API

The Data Export API supports project-scoped export of logs, metrics, usage, and execution-audit data. Logs, metrics, and usage exports are queued for background processing; execution exports are materialised synchronously on the create call.

All routes are mounted under /:project_id/v1/. Replace :project_id with your project's external id (for example proj_abc123). Authentication uses a project API key passed as Authorization: Bearer xero_my-project_....

Endpoints

Method Endpoint Description
POST /:project_id/v1/exports/logs Queue a request-log export. Returns 202 with status pending.
POST /:project_id/v1/exports/metrics Queue an aggregated-metrics export. Returns 202 with status pending.
POST /:project_id/v1/exports/usage Queue a usage/billing export. Returns 202 with status pending.
POST /:project_id/v1/exports/executions Materialise an execution-audit NDJSON export synchronously. Returns 202 with status completed on success.
GET /:project_id/v1/exports List export jobs. Query params: limit (default 20, max 100), offset (default 0), status (optional filter).
GET /:project_id/v1/exports/:id Get export job status.
GET /:project_id/v1/exports/:id/download Download a completed export. The response is always application/x-ndjson served as export-<uuid>.ndjson.
DELETE /:project_id/v1/exports/:id Cancel a pending or processing export. The job transitions to status cancelled.

Response Shape

Every endpoint that returns an export job uses the same DTO:

Field Type Description
id string (UUID) Server-generated bare UUID. Subsequent status/download calls must use this exact value.
export_type string One of logs, metrics, usage, executions.
format string The requested format (recorded but only jsonl/NDJSON is materialised in-tree).
status string See Export Job Statuses.
start_date / end_date string (ISO 8601) The bounds the job was created with (echoed back as response fields even though the request uses since/until).
filters object Filter blob persisted verbatim. No typed schema is enforced at the router boundary.
created_at / completed_at / failed_at string (ISO 8601), nullable Lifecycle timestamps.
error_message string, nullable Populated when status is failed.
download_url string, nullable Canonical download path. Present once the job is completed.

Export Types

All create-export bodies use since and until (ISO 8601) for the export window. The legacy keys start_date and end_date are not accepted on input and will be rejected with a 400 invalid_request_error.

Logs Export

Export request-level logs with optional filtering:

JSON
POST /:project_id/v1/exports/logs { "since": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z", "format": "jsonl", "filters": { "endpoint_ids": ["endpoint-prod"], "status_codes": [200, 429], "model_name": "llama-3.1-8b", "min_latency_ms": 100, "max_latency_ms": 5000, "region": "RegionOne" } }

Metrics Export

Export aggregated metrics:

JSON
POST /:project_id/v1/exports/metrics { "since": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z", "format": "jsonl" }

Usage Export

Export usage and billing data:

JSON
POST /:project_id/v1/exports/usage { "since": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z", "format": "jsonl" }

Executions Export

Export execution-audit rows as NDJSON. Unlike the other endpoints, this one materialises the payload synchronously on the create call: the response carries status completed (or failed) and the download is immediately available. All body fields are optional; an empty body or {} requests the default 24-hour window ending at the current time.

JSON
POST /:project_id/v1/exports/executions { "since": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z", "format": "jsonl", "filters": {} }

Filter Options

Filters are persisted verbatim as a free-form JSON object and applied by the export worker. The keys below are the worker-recognised shape today; the router does not enforce a typed filter schema, so unknown keys are accepted and stored but may be ignored downstream.

Filter Type Description
endpoint_ids array of strings Filter by endpoint slug(s).
status_codes array of integers Filter by HTTP response status codes.
model_name string Filter by model name.
min_latency_ms integer Minimum request latency in milliseconds.
max_latency_ms integer Maximum request latency in milliseconds.
region string Filter by region identifier.

The maximum date range for any export is 90 days. Requests with a wider range are rejected with a 400 error.

Export Formats

Format Content Type Description
jsonl application/x-ndjson One JSON object per line. The only format materialised in-tree today; recommended for all exports.
json application/x-ndjson Aspirational. Accepted by the create endpoints but the download always returns NDJSON.
csv application/x-ndjson Aspirational. Accepted by the create endpoints but the download always returns NDJSON.

The download endpoint always returns application/x-ndjson served as export-<uuid>.ndjson, regardless of the format recorded on the job. Content negotiation is not supported and Accept-Encoding: gzip is ignored.

Only the executions export materialises its payload synchronously on create. logs, metrics, and usage jobs are persisted in the pending state and remain queued until an out-of-tree worker picks them up.

Export Workflow

The general lifecycle for queued exports (logs, metrics, usage):

  1. Request, Submit an export request (POST /:project_id/v1/exports/logs, etc.). The router responds with 202 Accepted and a job DTO whose status is pending.
  2. Poll, Check the job (GET /:project_id/v1/exports/:id) until status is completed or failed.
  3. Download, Fetch the NDJSON file (GET /:project_id/v1/exports/:id/download).

The executions endpoint short-circuits steps 1 and 2: the create call returns 202 with status: completed (or failed) and the download is immediately available. The id on every job is a bare UUID; the status and download endpoints validate it as such and 404 on any other shape.

Export Job Statuses

Status Description
pending Export is queued for processing. This is the initial status for logs, metrics, and usage jobs.
processing Export is being generated by the worker.
completed Export is ready for download.
failed Export failed. Check error_message on the job DTO.
cancelled Export was cancelled via DELETE /:project_id/v1/exports/:id before completion.

Example Workflow

The examples below use proj_abc123 as a stand-in for the project's external id. Replace it with your own.

curl
# 1. Request an export (202 Accepted with status "pending") curl -X POST https://xerotier.ai/proj_abc123/v1/exports/logs \ -H "Authorization: Bearer xero_my-project_abc123" \ -H "Content-Type: application/json" \ -d '{ "since": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z", "format": "jsonl" }' # Response: {"id": "d3a1b2c4-5678-49ab-9cde-0123456789ab", "status": "pending", ...} # 2. Poll for completion curl https://xerotier.ai/proj_abc123/v1/exports/d3a1b2c4-5678-49ab-9cde-0123456789ab \ -H "Authorization: Bearer xero_my-project_abc123" # Response: {"id": "d3a1b2c4-...", "status": "completed", "download_url": "...", ...} # 3. Download the NDJSON file (always application/x-ndjson) curl -o export.ndjson \ https://xerotier.ai/proj_abc123/v1/exports/d3a1b2c4-5678-49ab-9cde-0123456789ab/download \ -H "Authorization: Bearer xero_my-project_abc123"
Python
import requests import time PROJECT_ID = "proj_abc123" BASE = f"https://xerotier.ai/{PROJECT_ID}/v1" headers = { "Authorization": "Bearer xero_my-project_abc123", "Content-Type": "application/json" } # 1. Request an export response = requests.post( f"{BASE}/exports/logs", headers=headers, json={ "since": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z", "format": "jsonl" } ) response.raise_for_status() # 202 Accepted export_id = response.json()["id"] # bare UUID # 2. Poll for completion while True: status_resp = requests.get(f"{BASE}/exports/{export_id}", headers=headers) status = status_resp.json()["status"] if status == "completed": break if status in ("failed", "cancelled"): raise Exception(f"Export {status}") time.sleep(5) # 3. Download the NDJSON payload download = requests.get(f"{BASE}/exports/{export_id}/download", headers=headers) download.raise_for_status() with open("export.ndjson", "wb") as f: f.write(download.content)
Node.js
const PROJECT_ID = "proj_abc123"; const BASE = `https://xerotier.ai/${PROJECT_ID}/v1`; const headers = { "Authorization": "Bearer xero_my-project_abc123", "Content-Type": "application/json" }; // 1. Request an export const createResp = await fetch( `${BASE}/exports/logs`, { method: "POST", headers, body: JSON.stringify({ since: "2026-01-01T00:00:00Z", until: "2026-01-31T23:59:59Z", format: "jsonl" }) } ); const { id: exportId } = await createResp.json(); // bare UUID // 2. Poll for completion let status; do { const statusResp = await fetch(`${BASE}/exports/${exportId}`, { headers }); const job = await statusResp.json(); status = job.status; if (status === "failed" || status === "cancelled") { throw new Error(`Export ${status}`); } if (status !== "completed") { await new Promise(r => setTimeout(r, 5000)); } } while (status !== "completed"); // 3. Download the NDJSON payload (always application/x-ndjson) const download = await fetch(`${BASE}/exports/${exportId}/download`, { headers }); const ndjson = await download.text(); console.log(`Exported ${ndjson.split("\n").filter(Boolean).length} records`);

GDPR Data Rights

The GDPR endpoints allow you to export all personal data associated with your account or request account deletion.

GDPR Endpoints

Method Endpoint Description
POST /gdpr/export Request a GDPR data export.
GET /gdpr/exports List your export requests (paginated).
GET /gdpr/export/:exportId Get export request status.
GET /gdpr/export/:exportId/download Download the export file.
DELETE /gdpr/export/:exportId Delete an export.
POST /gdpr/export/:exportId/cancel Cancel a pending export.

Export Status Values

Status Description
pending Export request created, processing not yet started.
processing Export is being generated.
completed Export is ready for download.
failed Export failed or was cancelled. Check errorMessage for details.
expired Export file has expired (download window has passed).
deleted Export was manually deleted by the user.

Synchronous best-effort: POST /gdpr/export runs payload generation inline and returns 202 Accepted with the final status set on the same response. The status is completed when generation and upload succeeded and failed otherwise, callers should inspect the returned status and errorMessage rather than assume success.

Cancelling a GDPR export via POST /gdpr/export/:exportId/cancel terminates the job but transitions it to failed, not cancelled. Only the deletion lifecycle (below) uses a distinct cancelled status.

Exported Data

A GDPR export includes the personal data associated with your account:

  • Project information
  • Model metadata
  • Endpoint configurations
  • API key metadata (not the key values themselves)
  • Usage events (capped at 10,000 records)
  • Invoices
  • Audit logs (capped at 10,000 records)

Exports are generated as JSON and stored securely in object storage. The download window and per-account export cadence are both 30 days by default; check with your deployment operator if you need different values.

Account Deletion

Account deletion is a two-step process with a grace period to prevent accidental data loss.

Deletion Endpoints

Method Endpoint Description
POST /gdpr/delete Request account deletion. Returns a confirmation token.
GET /gdpr/delete/:requestId Get deletion request status.
POST /gdpr/delete/:requestId/confirm Confirm deletion using the confirmation token. Starts the grace period.
POST /gdpr/delete/:requestId/cancel Cancel deletion during the grace period.
GET /gdpr/delete/preview Preview what data would be deleted.

Deletion Statuses

Status Description
pending Request created, awaiting confirmation token submission.
confirmed Token submitted; grace period is active. Cancellation is still possible.
processing Grace period has elapsed; deletion is in progress.
completed All project data has been permanently deleted.
cancelled Deletion was cancelled during the grace period.
failed Deletion encountered an error during processing.

Deletion Process

  1. Preview (optional), Use GET /gdpr/delete/preview to see a summary of what data would be removed before committing.
  2. Request, Submit a deletion request (POST /gdpr/delete). A secure confirmation token is generated and delivered via email (in production environments).
  3. Confirm, Submit the confirmation token to POST /gdpr/delete/:requestId/confirm with the body {"confirmationToken": "TOKEN"}. Confirmation starts the grace period.
  4. Grace period, The grace period is 30 days by default. You can cancel at any time during this window using POST /gdpr/delete/:requestId/cancel.
  5. Deletion, After the grace period expires, all account data is permanently deleted by a background job.

Before requesting deletion, use the preview endpoint to see exactly what data would be removed. Consider exporting your data first using the GDPR export feature.