Skip to main content

OJK Compliance API Reference

This reference documents the OJK endpoints registered in the current enterprise build. The module covers Indonesian regulatory frameworks: OJK AI Governance, UU PDP (Law 27/2022), and BI payment system (PBI 23/6 and 23/7).

caution

This reference is engineering documentation for teams building governed AI on AxonFlow. It is not legal advice. Validate the regulatory mapping with your legal and compliance teams before relying on it for OJK, BI, or UU PDP compliance.

Authentication

Every OJK route is scoped to the caller's organization:

  • X-Org-ID is the identifier. Behind the AxonFlow agent it is set from the cryptographically validated client credential, so a caller cannot choose it.
  • X-Tenant-ID is accepted only when X-Org-ID is absent. That fallback covers single-identifier deployments, where the two values are the same. When both headers are present the organization wins; the fallback never widens scope.
  • A request with neither header, or with a whitespace-only value, is refused with HTTP 400 and code missing_org. No query is issued with a blank scope.

Use X-User-ID when you want explicit attribution on export flows, breach notifications, and kill-switch actions.

Route overview

RouteMethod
/api/v1/ojk/audit/exportPOST
/api/v1/ojk/audit/export/{id}GET
/api/v1/ojk/audit/retentionGET
/api/v1/ojk/audit/readinessGET
/api/v1/ojk/breach/notifyPOST
/api/v1/ojk/breach/acknowledgePOST
/api/v1/ojk/breach/evaluate-deadlinesPOST
/api/v1/ojk/dashboardGET

Export API

Create export

POST /api/v1/ojk/audit/export

Supported request fields:

{
"start_date": "2026-01-01",
"end_date": "2026-03-31",
"data_types": ["policy_violations", "llm_calls", "decision_chain", "pii_redactions", "cross_border_transfers"],
"format": "json",
"framework": "OJK_AI_GOVERNANCE",
"include_pii": false,
"filters": {
"agent_ids": ["customer-copilot"],
"severity": ["high"],
"policy_types": ["pii"]
}
}

Request field details:

  • start_date and end_date - YYYY-MM-DD date strings defining the export window (RFC3339 timestamps are rejected with validation_error); the range cannot exceed 5 years

  • data_types - typed enum: policy_violations, llm_calls, decision_chain, hitl_oversight, pii_redactions, cross_border_transfers, breach_notifications, or all

  • format - json, csv or xml are all ACCEPTED, but this endpoint produces only JSON. A csv or xml request returns format: json (what the body is), plus requested_format and a format_note explaining it. The response never labels a JSON body as csv or xml.

  • framework - OJK_AI_GOVERNANCE, UU_PDP, BI_PJP, or OJK_BI_COMBINED. The label selects the report sections when data_types is omitted:

    FrameworkSections, in report order
    OJK_AI_GOVERNANCEpolicy_violations, llm_calls, decision_chain, hitl_oversight
    UU_PDPpii_redactions, cross_border_transfers, breach_notifications, policy_violations
    BI_PJPdecision_chain, hitl_oversight, pii_redactions, cross_border_transfers, breach_notifications
    OJK_BI_COMBINEDall seven sections
  • include_pii - boolean; PII is excluded unless explicitly requested

  • filters - optional object with agent_ids, user_ids, severity, and policy_types (all string arrays)

An explicitly requested data_types entry is always served, even when the selected framework does not consider it in scope; the section is returned with in_framework_scope: false and a note rather than being dropped. A data type the platform does not recognise produces an explicit per-section error, never a silent empty section.

Response summary shape

{
"summary": {
"total_records": 12,
"records_by_type": { "pii_redactions": 4, "decision_chain": 8 },
"report_state": "populated",
"sections": [
{
"data_type": "pii_redactions",
"report_state": "populated",
"record_count": 4,
"in_framework_scope": true,
"note": "Processing record for Indonesian personal data (NIK, NPWP, contact, bank account) with the action taken."
},
{
"data_type": "breach_notifications",
"report_state": "enabled_empty",
"record_count": 0,
"in_framework_scope": true
}
],
"framework_summary": {
"framework": "UU_PDP",
"citation": "UU PDP Law 27/2022, Pasal 56 (cross-border transfer) and Art. 46 (3x24h breach notification)",
"pillars": []
}
}
}

report_state is carried per section and rolled up on the summary:

ValueMeaning
populatedServed, at least one record
enabled_emptyServed, and the honest answer is zero records for this organization and window
not_availableCould not be served at all. Always paired with an error and an error_kind of section_not_implemented, store_absent or query_failed

Read report_state, not the presence of a JSON key: every data array is omitempty, so absence alone is ambiguous by construction.

Sections are capped at 100,000 records. A section that reaches the cap says so in its note.

metadata.export_version is 2.0.0 for this contract. A 1.0.0 consumer that inferred "module not enabled" from an empty body must move to report_state.

PII redaction records

{
"id": "…",
"timestamp": "2026-07-15T10:00:00Z",
"pii_type": "nik",
"ojk_category": "national_identity",
"severity": "critical",
"masked_value": "31**********0001",
"redaction_method": "indonesia_detector_mask",
"confidence": 0.7,
"action": "blocked",
"plane": "gateway",
"decision_id": "…",
"correlation_id": "…"
}
  • ojk_category - national_identity, tax_identifier, contact_information, financial_account
  • action - blocked (request refused), redacted (this plane masked the value), redaction_required (a policy decision point required redaction and told the PEP; the gateway pre-check and /api/v1/decide are PDPs and the calling SDK performs the mask), detected (observed under a warn/log posture, content unmodified)
  • plane - gateway, decision, mcp

Raw detected values are never stored and therefore can never be exported. This section requires enterprise migration 137; without it the section reports not_available with an error naming the migration.

Get export status

GET /api/v1/ojk/audit/export/{id}

The response includes:

  • export_id
  • status
  • framework
  • summary
  • download_url
  • expires_at
  • metadata

Retention API

Get retention status

GET /api/v1/ojk/audit/retention

The retention response includes:

  • overall compliance_status
  • framework
  • retention_days (the configured retention period) and min_retention_days (the enforced floor)
  • a data_types array (the shape reserves one entry per audit data type - data_type, status, optional oldest_record / newest_record, total_records - but the current implementation always returns it empty)
  • optional next_cleanup

The OJK module enforces a 5-year minimum retention floor when AXONFLOW_COMPLIANCE_REGION includes ID.

Readiness API

Get readiness

GET /api/v1/ojk/audit/readiness

The readiness response shape:

{
"ready": false,
"score": 70,
"framework": "OJK_BI_COMBINED",
"measured_checks": 5,
"unknown_checks": 0,
"checks": [
{
"name": "Data Retention",
"description": "OJK requires minimum 5-year retention of AI decision records",
"status": "pass",
"details": "Retention configured at 3650 days (minimum 1825).",
"observed": 3650
},
{
"name": "PII Detection",
"description": "NIK, NPWP, and bank-account detection must be active per UU PDP",
"status": "warning",
"details": "1 enabled Indonesia PII policies are visible, but no detection event was recorded in the last 90 days. The control is configured; this window contains no evidence that it fired.",
"observed": 0
},
{
"name": "Human Oversight",
"description": "OJK AI Governance requires human oversight for material decisions",
"status": "fail",
"details": "3 oversight requests were queued in the last 90 days and NONE was reviewed. Gated decisions are not receiving human review.",
"observed": 0
}
],
"recommendations": ["Human Oversight: 3 oversight requests were queued …"]
}

Every check queries the state it names, or reports unknown. A dimension the platform cannot observe from this deployment is never reported as a pass.

CheckWhat is measured
Data RetentionEffective retention configuration against the 1825-day Indonesian floor
PII DetectionEnabled pii-indonesia policy rows visible to the organization, plus detection events in the last 90 days
Human OversightOversight requests queued in the last 90 days, and how many were reviewed
Audit LoggingAudit records for the organization in the last 90 days
Breach NotificationRecorded breaches past the 3x24 hour window without a timely submission

Each check contains:

  • name
  • description
  • status - pass, warning, fail, or unknown
  • details - what was OBSERVED, not what is assumed
  • observed - the raw measurement behind the verdict, when there is one

Scoring: a pass counts 1, a warning 0.5, a failure 0; an unknown check scores zero while still counting in the denominator, so an unobservable dimension drags the score down rather than inflating it. measured_checks / unknown_checks expose the split. ready requires no failures, no unknowns, and a score of at least 80.

  • optional details

Breach Notification API

Generate breach notification (UU PDP Art. 46)

POST /api/v1/ojk/breach/notify

Request fields (all required):

{
"incident_timestamp": "2026-03-15T09:10:00Z",
"discovery_time": "2026-03-15T14:30:00Z",
"data_subjects_affected": 1200,
"data_types_involved": ["nik", "npwp", "bank_account"],
"description": "Credential leak exposed customer identity records",
"remediation_steps": [
"Revoked compromised API credentials",
"Enabled additional PII redaction policies",
"Initiated forensic audit of affected tenant"
]
}

Request field details:

  • incident_timestamp - RFC3339 timestamp of the breach event itself
  • discovery_time - RFC3339 timestamp when the breach was discovered (the notification deadline is calculated as 72 hours from this)
  • data_subjects_affected - positive integer count of affected data subjects
  • data_types_involved - array of personal data types that were breached
  • description - free-text incident description (UU PDP Art. 46)
  • remediation_steps - array of strings describing actions taken or planned

The handler returns 201 Created with the stored notification:

  • id - unique tracking identifier
  • notification_deadline - 72 hours from discovery_time
  • notified_authority - defaults to "MOCDA" (current notification recipient, pending DPA constitution)
  • status - the effective lifecycle status: submitting via this endpoint yields submitted, or overdue if the 72-hour window had already lapsed at submission
  • submitted_at, created_at - server timestamps
  • the request fields echoed back

Acknowledge breach notification

POST /api/v1/ojk/breach/acknowledge

Records authority receipt for a previously submitted breach. Body: {"id": "<notification-id>"}. Returns the updated notification; 404 not_found for an unknown id and 409 invalid_transition when the notification is not in an acknowledgeable state.

Evaluate breach deadlines

POST /api/v1/ojk/breach/evaluate-deadlines

Durably flips never-submitted draft notifications whose 72-hour window has lapsed to overdue. Returns {"flipped_overdue": <count>}.

Lifecycle statuses across the breach endpoints: draftsubmittedacknowledged, with overdue for a lapsed deadline and failed as the terminal error state.

Dashboard API

Get dashboard

GET /api/v1/ojk/dashboard

The dashboard returns organization-scoped counts, each derived from a query:

FieldDerivation
compliance_scoreThe readiness score, so this and /audit/readiness cannot disagree. readiness_unknown_checks reports unread dimensions; non-zero adds compliance_score:partial to unavailable
total_audit_recordsAudit records for the organization, all time
active_policiesEnabled Indonesia-PII policy rows the organization can see (global system tier plus its own)
recent_violationsBlocked, redacted or needs-approval decisions in the last 30 days
indonesia_pii_eventsIndonesia PII detection events in the last 30 days
breach_notifications / overdue_breach_notificationsRecorded breaches, and how many are past the 3x24 hour window
retention_statusDerived from the Data Retention readiness check

A count that cannot be derived on this deployment is returned as -1 and named in an unavailable array. "We could not measure this" and "there were none" are different answers, so they are different values.

Error responses

All OJK endpoints return a structured error body:

{
"code": "validation_error",
"message": "end_date must be after start_date",
"details": "optional additional context"
}

Common error codes:

StatuscodeWhen
400invalid_request / validation_errorMalformed body or invalid request parameters
400missing_tenantNo tenant context resolved for the request
404not_foundExport or breach-notification ID does not exist
409invalid_transitionBreach notification cannot move to the requested lifecycle state
500internal_errorServer-side failure