Skip to content

Exports API

The Exports API provides endpoints for exporting workbooks to multiple formats with cryptographic provenance metadata for audit trails.


Overview

CalcBridge's export system supports:

  • Multi-format export: XLSX, CSV, JSON (PDF is available as a sealed artifact for XLSX exports)
  • Provenance hashing: canonical hash + HMAC signature embedded in XLSX exports
  • Custom XML parts: embedded provenance manifest in XLSX files
  • Security: zip bomb protection, PII redaction, antivirus validation

Endpoints

Create Export

Create a new export job for a workbook.

POST /api/v1/exports

Request Body

Field Type Required Description
workbook_id string (UUID) Yes Workbook to export
format string Yes Export format: xlsx, csv, json, pdf
sheet_ids string[] No Specific sheet IDs to include (all if omitted)
options object No Format-specific options

Options Object

Field Type Default Description
xlsx_include_styles boolean true Preserve cell styles in XLSX
xlsx_freeze_header boolean true Freeze header row in XLSX
xlsx_auto_width boolean true Auto-fit column widths in XLSX
xlsx_three_zone boolean true Generate Foundation/Workspace/Control layout
xlsx_sealed_pdf boolean false Generate sealed PDF artifact alongside XLSX
xlsx_signing boolean false Sign XLSX via external signing service
csv_delimiter string "," CSV delimiter
csv_encoding string "utf-8-sig" CSV encoding
json_pretty_print boolean true Pretty-print JSON output
sanitize_pii boolean true Apply PII redaction
classification_threshold string confidential Minimum classification level to redact
redaction_strategy string mask Redaction strategy: mask, hash, remove

Example Request

curl -X POST https://api.calcbridge.io/api/v1/exports \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "workbook_id": "550e8400-e29b-41d4-a716-446655440000",
    "format": "xlsx",
    "options": {
      "xlsx_three_zone": true,
      "xlsx_sealed_pdf": true,
      "sanitize_pii": true
    }
  }'
import requests

response = requests.post(
    "https://api.calcbridge.io/api/v1/exports",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "workbook_id": "550e8400-e29b-41d4-a716-446655440000",
        "format": "xlsx",
        "options": {
            "xlsx_three_zone": True,
            "xlsx_sealed_pdf": True,
            "sanitize_pii": True
        }
    }
)
export = response.json()
print(f"Export job: {export['id']} - Status: {export['status']}")
const response = await fetch('https://api.calcbridge.io/api/v1/exports', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    workbook_id: '550e8400-e29b-41d4-a716-446655440000',
    format: 'xlsx',
    options: {
      xlsx_three_zone: true,
      xlsx_sealed_pdf: true,
      sanitize_pii: true
    }
  })
});
const exportJob = await response.json();
console.log('Export job:', exportJob.id, '- Status:', exportJob.status);

Response

{
  "id": "exp_abc123",
  "workbook_id": "550e8400-e29b-41d4-a716-446655440000",
  "format": "xlsx",
  "status": "processing",
  "file_size_bytes": null,
  "download_url": null,
  "expires_at": null,
  "progress": {
    "current": 0,
    "total": 0,
    "message": "Queued",
    "percent": 0
  },
  "result_summary": null,
  "error_details": null,
  "created_at": "2026-02-15T10:30:00Z",
  "started_at": null,
  "completed_at": null,
  "is_downloadable": false
}

Error Responses

Status Error Description
400 Invalid request Bad sheet IDs or options
403 Access denied Not authorized to export this workbook
404 Workbook not found Invalid workbook_id

List Exports

List export jobs for the current tenant.

GET /api/v1/exports

Query Parameters

Parameter Type Default Description
skip integer 0 Records to skip
limit integer 50 Max records to return (1-100)
workbook_id string (UUID) - Filter by workbook
status string - Filter by status: pending, processing, completed, failed, cancelled

Response

{
  "items": [
    {
      "id": "exp_abc123",
      "workbook_id": "550e8400-e29b-41d4-a716-446655440000",
      "format": "xlsx",
      "status": "completed",
      "file_size_bytes": 2458624,
      "download_url": "https://files.calcbridge.com/exports/exp_abc123.xlsx",
      "expires_at": "2026-02-16T10:30:00Z",
      "progress": {
        "current": 1,
        "total": 1,
        "message": "Completed",
        "percent": 100
      },
      "result_summary": {
        "sealed_pdf": { "storage_path": "exports/exp_abc123.pdf" },
        "provenance": { "manifest_version": "cb_hash_v1", "hash_scope": "foundation" }
      },
      "error_details": null,
      "created_at": "2026-02-15T10:30:00Z",
      "started_at": "2026-02-15T10:30:05Z",
      "completed_at": "2026-02-15T10:31:10Z",
      "is_downloadable": true
    }
  ],
  "total": 1,
  "skip": 0,
  "limit": 50
}

Get Export

Get details and status of an export job.

GET /api/v1/exports/{export_id}

Path Parameters

Parameter Type Description
export_id string Export job identifier

Response

Returns the same payload as a list item (see List Exports).


Download Export

Download the exported file if ready.

GET /api/v1/exports/{export_id}/download

Query Parameters

Parameter Type Default Description
artifact string - Optional artifact to download. Currently supported: pdf

Notes

  • If artifact=pdf and no sealed PDF was generated, the API returns 404.
  • Exports are time-limited; expired exports return 410.

Verify Export

Verify the integrity of an exported XLSX file using its embedded provenance metadata.

POST /api/v1/exports/verify

Request Body (multipart/form-data)

Field Type Required Description
file file Yes XLSX export file to verify

Response

{
  "status": "original",
  "export_id": "exp_abc123",
  "workbook_id": "550e8400-e29b-41d4-a716-446655440000",
  "tenant_id": "t_abc123",
  "data_hash": "sha256:...",
  "expected_hash": "sha256:...",
  "hmac_valid": true,
  "manifest_version": "cb_hash_v1",
  "hash_scope": "foundation",
  "diffs": [
    { "sheet": "Foundation - Assets", "cell": "C12", "status": "modified" }
  ],
  "diffs_truncated": false
}

Status Values

Status Meaning
original Hash matches server record
modified Hash mismatch detected
invalid_provenance Manifest invalid or signature mismatch
unknown No manifest or export record found

Error Responses

Status Error Description
400 Invalid request Bad file type or empty file
403 Forbidden Tenant mismatch (authenticated)
413 Too large File exceeds size limits

Cancel Export

Cancel a pending or processing export job.

DELETE /api/v1/exports/{export_id}

Response

Returns 204 No Content on success.


List Export Formats

Get list of supported export formats and capabilities.

GET /api/v1/exports/formats/list

Response

[
  {
    "format": "xlsx",
    "name": "Excel Workbook",
    "description": "Microsoft Excel workbook with styles and formatting",
    "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "extension": ".xlsx",
    "multi_sheet": true
  },
  {
    "format": "csv",
    "name": "CSV",
    "description": "Comma-separated values (multi-sheet exports as ZIP)",
    "mime_type": "text/csv",
    "extension": ".csv",
    "multi_sheet": false,
    "notes": "Single sheet exports to .csv, multiple sheets to .zip"
  },
  {
    "format": "json",
    "name": "JSON",
    "description": "Structured JSON with metadata and all sheets",
    "mime_type": "application/json",
    "extension": ".json",
    "multi_sheet": true
  }
]

Export Status

Status Description
pending Export job is queued
processing Export is being generated
completed Export is ready for download
failed Export generation failed
cancelled Export was cancelled by user

Provenance Metadata

XLSX exports include a provenance manifest embedded as a Custom XML Part. The server also stores a manifest record for verification.

  • result_summary.provenance.manifest_version indicates the canonical hash version (currently cb_hash_v1).
  • result_summary.provenance.hash_scope indicates which sheets were hashed (foundation or validated).
  • Sealed PDFs, when requested, appear under result_summary.sealed_pdf.