---
title: "Export SEBI audit data"
method: POST
path: "/api/v1/sebi/audit/export"
tags: ["SEBI Compliance"]
---

# Export SEBI audit data

`POST /api/v1/sebi/audit/export`

## Request

- Base URL: `https://orchestrator.getaxonflow.com`
- URL: `https://orchestrator.getaxonflow.com/api/v1/sebi/audit/export`
- Auth: none declared
- TypeScript: <https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/docs/api/v1/sebi/audit/export/post.ts> (a function with typed inputs; its response is checked against the schema)

Export audit data for SEBI regulatory submission. Supports:
- Multiple compliance frameworks (SEBI AI/ML, DPDP, Combined)
- Various data types (policy violations, LLM calls, decision chain, HITL, PII redactions)
- Multiple export formats (JSON, CSV, XML)
- Optional PII redaction for external auditors

**This endpoint is SYNCHRONOUS.** The export data is collected and returned
in this response; there is no job to poll and no separate download URL.
(An earlier version of this text said large exports were processed
asynchronously and told callers to poll a status endpoint. That was never
true - the status route queried a table no migration creates, so it
returned 500 on every deployment. See #3246.)

For an ASYNCHRONOUS, downloadable regulatory artifact use
`POST /api/v1/compliance/reports` with `regulator=sebi`.

**5-Year Retention**: Per SEBI AI/ML Guidelines, all audit data is retained
for minimum 5 years (1825 days).

## Request body

- SEBIAuditExportRequest: Request to export SEBI audit data
  - `start_date` string, date-time, required: Start of export period (inclusive)
  - `end_date` string, date-time, required: End of export period (inclusive)
  - `data_types` SEBIAuditDataType[]: Types of audit data to export (defaults to all)
  - `format` 'json' | 'csv' | 'xml': Export output format
  - `framework` 'SEBI_AI_ML' | 'DPDP_ACT_2023' | 'SEBI_DPDP_COMBINED': SEBI compliance framework identifier
  - `include_archived` boolean: Include records from cold storage
  - `redact_pii` boolean: Redact PII in export (for external auditors)
  - `filters` SEBIAuditExportFilters: Optional filters for audit exports
    - `agent_ids` string[]: Filter by agent IDs
    - `user_ids` integer[]: Filter by user IDs
    - `severity` string: Minimum severity level
    - `policy_types` string[]: Filter by policy types
    - `violation_types` string[]: Filter by violation types
    - `include_model_info` boolean: Include detailed model information

## Response `200`

The export, synchronously. There is no queued state: the body IS
the data. `status` is completed, partial or failed.

- SEBIAuditExportResponse: Response for audit export requests
  - `export_id` string: Unique export identifier
  - `status` 'completed' | 'partial' | 'failed': Export status. SEBI audit export is SYNCHRONOUS - the POST returns the data - so there is no queued state: `pending` and `processing` were in this enum and the server has never been able to emit either (sebi_audit_export_service.go assigns the field unconditionally from sebiExportStatus on every path that returns 200). `completed` ONLY when every requested section was served AND every served section's scope could reach the dimensions its writer stamps. `partial` when at least one but not every section is missing or scope-gapped; `failed` when none could be produced. On `partial` and `failed`, `summary.compliance_score` is ABSENT rather than zero.
  - `exported_at` string, date-time: When the export was completed
  - `framework` 'SEBI_AI_ML' | 'DPDP_ACT_2023' | 'SEBI_DPDP_COMBINED': SEBI compliance framework identifier
  - `summary` SEBIAuditExportSummary: Statistics about the export
    - `total_records` integer: Total records exported
    - `records_by_type` object: Records by data type
    - `date_range` object
      - `start` string, date-time
      - `end` string, date-time
    - `violations_summary` SEBIViolationsSummary: Policy violations summary
      - `total` integer: Total violations
      - `by_severity` object
        - `critical` integer
        - `high` integer
        - `medium` integer
        - `low` integer
      - `by_type` object: Violations by type
      - `trend` 'improving' | 'stable' | 'degrading': Trend direction
    - `compliance_score` number, float, nullable: Compliance score for the period, or ABSENT (the key is omitted) when the export could not support one (#3435). It is derived from the violations this export actually read, so it has no basis when a requested section could not be produced, or was produced under a tenancy scope that cannot reach its rows. Reporting 100.00 in either case is an affirmative regulatory claim manufactured out of a read that did not happen. Same contract as avg_latency_ms elsewhere in this spec: absence is the absence of a measurement, NOT a measured zero, and a strict deserializer binding this to a non-optional float WILL raise. Check `report_state` first.
    - `report_state` 'not_available' | 'enabled_empty' | 'populated' | 'partial': The document-level roll-up of the per-section outcomes (#3435). `populated` and `enabled_empty` are only reported when EVERY requested section was served AND every served section's scope could reach the dimensions its writer stamps. `partial` means at least one section could not be produced, or was produced under a scope that cannot reach part of its population; `compliance_score` is absent in that case. `not_available` means no requested section could be produced.
    - `section_row_total` integer: The plain sum of the per-section counts. `total_records` is the DISTINCT record count, so total_records + overlapping_records == section_row_total.
    - `overlapping_records` integer: Records that are members of more than one section and are therefore counted ONCE in total_records. A governed LLM call is one audit_logs row carrying both plane='llm' and a decision id, so it belongs to both llm_calls and decision_chain.
    - `sections` SEBISectionStatus[]: One honest outcome per REQUESTED data type, in request order.
      - `data_type` string: The requested section.
      - `report_state` 'not_available' | 'enabled_empty' | 'populated': This section's three-state outcome. `partial` is a document-level state only and never appears here.
      - `record_count` integer: Rows in the section. Always 0 when `error` is set; a reader must consult report_state before believing it.
      - `error` string: The failure this section hit, verbatim. Always pairs with report_state=not_available, and the section is then omitted from records_by_type entirely rather than recorded as a zero.
      - `error_kind` 'store_absent' | 'query_failed' | 'section_not_implemented': Classifies the failure, because report_state alone cannot. `store_absent` is 42P01 only: an undefined COLUMN (42703) is schema drift and lands in `query_failed`, because telling an operator to apply a migration that is already applied sends them to the wrong place. `not_configured` is NOT in this enum, and the difference from SEBIDataTypeRetentionStatus.error_kind is deliberate (#3435 R8). Both fields are filled by one classifier (`classifySectionError`), but they classify DIFFERENT error populations, so listing the classifier's whole range on both schemas documented a value each one cannot carry. Here the input is an export section's failure: `errRetentionNotConfigured` is constructed at exactly one site, inside `getRetentionConfig`, which only the retention endpoint calls, so `not_configured` is unreachable on an export section. The mirror value `section_not_implemented` is unreachable on the retention row for the same reason in the other direction.
      - `scope_gap` SEBIScopeGap, nullable: Present when the section's read SUCCEEDED but ran under a tenancy scope carrying no identifier for a dimension its writer stamps (#3435). It is a third answer, distinct from both "could not be produced" and "honestly empty": the query ran, under a key that cannot reach part or all of the population. A section carrying this forces the document to `partial`, which suppresses `compliance_score` and stops the export reporting status `completed`. Integrators MUST NOT read a `scope_gap` section as a statement that nothing occurred.
        - `dimension` 'client' | 'org': The missing tenancy dimension.
        - `column` string: The identity column whose population is out of reach, so a reader can check the claim against the schema rather than take it.
        - `reason` string: The plain-language statement carried into the artifact.
  - `download_url` string: Presigned URL to download the export from cloud storage
  - `expires_at` string, date-time: When the download URL expires
  - `storage_type` 'local' | 's3' | 'gcs' | 'azure': Storage backend used for this export
  - `storage_key` string: Cloud storage object key (path within bucket/container)
  - `file_size_bytes` integer: Size of the export file in bytes
  - `file_checksum` string: SHA-256 checksum of the export file
  - `metadata` SEBIExportMetadata: Export metadata for audit trail
    - `export_version` string: Schema version
    - `generated_by` string: System that generated the export
    - `generated_at` string, date-time
    - `tenant_id` string: The scope key the export was produced for. Documented as `org_id: integer` and never emitted under that name or that type (#3435 R8): the struct field is `TenantID string \`json:"tenant_id"\`` (SEBIExportMetadata). Same correction as SEBIRetentionResponse.tenant_id.
    - `org_name` string
    - `scope_key` string: Every tenancy identifier the sections were actually matched against, rendered per dimension (#3435). It is in the artifact because each section's empty-state sentence is only true RELATIVE to the identifiers searched, and a reader cannot check that without being told what they were.
    - `requested_by` string: User who requested the export
    - `compliance_framework` 'SEBI_AI_ML' | 'DPDP_ACT_2023' | 'SEBI_DPDP_COMBINED': SEBI compliance framework identifier
    - `retention_days` integer: The retention period SEBI REQUIRES, in days (1825 = 5 years). Read it with `retention_configured` below. It is assigned at response construction before any query runs and is never overwritten, so it is not a measurement of this deployment (#3435 R8).
    - `retention_configured` boolean: Whether `retention_days` was read from this organisation's configuration. ALWAYS `false` on this object: the export path does not consult `audit_retention_config`. `GET /api/v1/sebi/audit/retention` answers what is configured, per data type, and reports `UNKNOWN` when nothing is.
    - `checksum` string: SHA-256 checksum of export data
    - `signed_by` string: Signing authority (for signed exports)

## Other responses

- `400`: Invalid request (coded envelope)

---

[API](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api.md) - [All operations](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/revisions/f408ef3ec9dd?raw)
