{"openapi":"3.0.0","paths":{"/v1/events":{"get":{"operationId":"V1EventsController_list","summary":"List security events (keyset-paginated, newest first)","description":"Returns OCSF-shaped events for your business. Use `next_cursor` as `?cursor=` on the next request to page; the cursor is opaque and signed.","parameters":[{"name":"type","required":false,"in":"query","description":"Filter to events of a particular type. One of `policy_violation`, `dlp_redaction`, `discovery`. Omit to receive all.","schema":{"type":"string"}},{"name":"since","required":false,"in":"query","description":"ISO-8601 lower bound (inclusive).","schema":{"format":"date-time","type":"string"}},{"name":"until","required":false,"in":"query","description":"ISO-8601 upper bound (exclusive).","schema":{"format":"date-time","type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque pagination cursor from a previous response's `next_cursor`. Do not construct manually — cursors are HMAC-signed.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size. Max 100; default 50.","schema":{"minimum":1,"maximum":100,"default":50,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedEventsResponseDto"}}}}},"tags":["events"],"security":[{"ApiKey":[]}]}},"/v1/events/{id}":{"get":{"operationId":"V1EventsController_getById","summary":"Get a single event by id","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1EventDto"}}}},"404":{"description":"Event not found."}},"tags":["events"],"security":[{"ApiKey":[]}]}},"/v1/guardrails/{id}/test":{"post":{"operationId":"V1GuardrailsController_test","summary":"Run a single guardrail against an ad-hoc input","description":"Evaluates the named guardrail (PII / regex / LLM / external) against the input string and returns the breaches and redactions it would generate. No event is recorded; this is a stateless probe.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestGuardrailBodyDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuardrailTestResponseDto"}}}},"404":{"description":"Guardrail not found in this business."}},"tags":["guardrails"],"security":[{"ApiKey":[]}]}},"/v1/frameworks/{id}/test":{"post":{"operationId":"V1FrameworksController_test","summary":"Run a framework against an ad-hoc input","description":"Evaluates every guardrail attached to the framework against the input and returns the combined breaches/redactions. Stateless — no event recorded.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestFrameworkBodyDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrameworkTestResponseDto"}}}},"404":{"description":"Framework not found in this business."}},"tags":["frameworks"],"security":[{"ApiKey":[]}]}},"/v1/frameworks/{id}/test-file":{"post":{"operationId":"V1FrameworksController_testFile","summary":"Run a framework against an uploaded file","description":"Multipart variant of `POST /v1/frameworks/:id/test`. For documents the file is parsed to text and run through every attached guardrail; for images the binary is sent to image guardrails. Stateless (no event recorded). For documents with redactable entities, and for images/documents where a fired Signature Vision guardrail has a redacting action, the response includes an optional `redacted_file` (base64) so callers can preview/download the redacted copy; nothing is persisted. Signature copies are returned only after Orbital proves every verified visual region was applied. Building a copy is expensive for some formats. Pass `?defer_redacted_file=true` to return the verdict immediately without `redacted_file`; `redacted_file_available` then tells you whether a redacted copy exists to fetch on demand via `POST /v1/frameworks/:id/test-file/redacted`.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"defer_redacted_file","required":false,"in":"query","schema":{"type":"boolean"},"description":"When `true`, skip generating the (potentially slow) redacted copy inline. The verdict returns immediately with `redacted_file` omitted; use `redacted_file_available` and POST /v1/frameworks/:id/test-file/redacted to fetch it. Defaults to false (redacted copy inlined), preserving the original response."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"known_entities":{"type":"string","description":"Optional JSON-encoded known_entities map."},"redaction_style":{"type":"string","enum":["placeholder","realistic"],"description":"Optional per-request redaction style. `placeholder` replaces every redacted entity with the numbered `<TYPE_N>` placeholder; `realistic` replaces it with a same-type realistic substitute where one exists (types without one keep `<TYPE_N>`). Omit it, or send an unrecognized value, to keep the redaction style the framework's guardrails are configured for. Also accepted as the `X-Orbital-Redaction-Style` header; the body wins. Additive, optional field."}},"required":["file"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrameworkFileTestResponseDto"}}}},"400":{"description":"Missing or unsupported file."},"404":{"description":"Framework not found in this business."}},"tags":["frameworks"],"security":[{"ApiKey":[]}]}},"/v1/frameworks/{id}/test-file/redacted":{"post":{"operationId":"V1FrameworksController_testFileRedacted","summary":"Generate the redacted copy of an uploaded file on demand","description":"Companion to `POST /v1/frameworks/:id/test-file?defer_redacted_file=true`. Re-upload the same file to get just the (potentially slow) redacted copy without blocking the verdict. Stateless: it re-runs the same guardrails on the same input, so the redactions match the deferred verdict. Signature-redacting guardrails can produce copies for supported images as well as documents. Returns `redacted_file` (base64) or null when nothing is redactable, application proof is incomplete, the format is unsupported, or the engine could only return the original (an un-redacted original is never returned). Nothing is persisted.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"known_entities":{"type":"string","description":"Optional JSON-encoded known_entities map."},"redaction_style":{"type":"string","enum":["placeholder","realistic"],"description":"Optional per-request redaction style. `placeholder` replaces every redacted entity with the numbered `<TYPE_N>` placeholder; `realistic` replaces it with a same-type realistic substitute where one exists (types without one keep `<TYPE_N>`). Omit it, or send an unrecognized value, to keep the redaction style the framework's guardrails are configured for. Also accepted as the `X-Orbital-Redaction-Style` header; the body wins. Additive, optional field."}},"required":["file"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrameworkFileRedactedResponseDto"}}}},"400":{"description":"Missing or unsupported file."},"404":{"description":"Framework not found in this business."}},"tags":["frameworks"],"security":[{"ApiKey":[]}]}},"/v1/users":{"get":{"operationId":"V1UsersController_list","summary":"List users in the business (keyset-paginated)","parameters":[{"name":"cursor","required":false,"in":"query","description":"Cursor from a previous response. Opaque, HMAC-signed.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":50,"type":"number"}},{"name":"email_contains","required":false,"in":"query","description":"Substring filter on email (case-insensitive).","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"enum":["active","waiting_seat"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedUsersResponseDto"}}}}},"tags":["users"],"security":[{"ApiKey":[]}]}},"/v1/users/{id}":{"get":{"operationId":"V1UsersController_getById","summary":"Get a single user by id","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1UserDto"}}}},"404":{"description":"User not found in this business."}},"tags":["users"],"security":[{"ApiKey":[]}]}},"/v1/users/{id}/onboarding":{"get":{"operationId":"V1UsersController_getOnboarding","summary":"Get a user’s onboarding completion state","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStatusDto"}}}}},"tags":["users"],"security":[{"ApiKey":[]}]},"put":{"operationId":"V1UsersController_setOnboarding","summary":"Set a user’s onboarding completion (idempotent)","description":"`{ \"completed\": true }` marks the user complete; `{ \"completed\": false }` resets the completion. Idempotent — returns the resulting state.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetOnboardingBodyDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStatusDto"}}}}},"tags":["users"],"security":[{"ApiKey":[]}]}},"/v1/analytics/usage-summary":{"get":{"operationId":"V1AnalyticsController_usageSummary","summary":"Usage rollup over a time window, bucketed by tool / user / action / day","description":"Returns counts of prompts (total, blocked, auto-redacted, warned, flagged, no-policy, compliant) for your business between `since` and `until`, grouped by the requested dimension. Designed to feed dashboard tiles in Sentinel / Splunk / Datadog without scanning the events feed.","parameters":[{"name":"since","required":true,"in":"query","description":"ISO-8601 inclusive lower bound.","schema":{"format":"date-time","type":"string"}},{"name":"until","required":true,"in":"query","description":"ISO-8601 exclusive upper bound.","schema":{"format":"date-time","type":"string"}},{"name":"group_by","required":true,"in":"query","description":"Dimension to bucket counts by. `tool` = platform_id, `user` = user_id, `action` = the policy action enum, `day` = UTC date.","schema":{"enum":["tool","user","action","day"],"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max buckets. 1–1000, default 100.","schema":{"minimum":1,"maximum":1000,"default":100,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSummaryResponseDto"}}}}},"tags":["analytics"],"security":[{"ApiKey":[]}]}},"/v1/analytics/violations-summary":{"get":{"operationId":"V1AnalyticsController_violationsSummary","summary":"Policy violations over a time window, bucketed by tool / user / severity / day","description":"Restricts the population to rows where a policy actually fired (BLOCK / AUTO_REDACT / WARN / FLAG) and rolls them up. Severity assignment is platform-defined and stable: BLOCK → critical, AUTO_REDACT → high, WARN → medium, FLAG → low.","parameters":[{"name":"since","required":true,"in":"query","description":"ISO-8601 inclusive lower bound.","schema":{"format":"date-time","type":"string"}},{"name":"until","required":true,"in":"query","description":"ISO-8601 exclusive upper bound.","schema":{"format":"date-time","type":"string"}},{"name":"group_by","required":true,"in":"query","description":"Dimension to bucket violations by. `tool` = platform_id, `user` = user_id, `severity` = critical/high/medium/low (BLOCK / AUTO_REDACT / WARN / FLAG respectively), `day` = UTC date.","schema":{"enum":["tool","user","severity","day"],"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max buckets. 1–1000, default 100.","schema":{"minimum":1,"maximum":1000,"default":100,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViolationsSummaryResponseDto"}}}}},"tags":["analytics"],"security":[{"ApiKey":[]}]}}},"info":{"title":"Aona Public API","description":"The customer-facing /v1 surface of the Aona Workforce AI Security platform. Events are emitted in OCSF (Open Cybersecurity Schema Framework) shape.","version":"1.0","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-Api-Key"}},"schemas":{"OcsfActorUserDto":{"type":"object","properties":{"uid":{"type":"string","description":"Aona user UUID, if known.","nullable":true}},"required":["uid"]},"OcsfActorDto":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/OcsfActorUserDto"}},"required":["user"]},"OcsfAppDto":{"type":"object","properties":{"name":{"type":"string","description":"Static product label.","example":"Aona"},"uid":{"type":"string","description":"AI platform UUID (e.g. ChatGPT, Claude).","nullable":true}},"required":["name","uid"]},"OcsfProductDto":{"type":"object","properties":{"name":{"type":"string","example":"Aona Workforce AI Security"}},"required":["name"]},"OcsfMetadataDto":{"type":"object","properties":{"product":{"$ref":"#/components/schemas/OcsfProductDto"},"tenant_uid":{"type":"string","description":"Aona business (tenant) UUID."}},"required":["product","tenant_uid"]},"OcsfEnrichmentDto":{"type":"object","properties":{"name":{"type":"string","description":"Enrichment name. Aona uses: `data_security_risk`, `data_privacy_risk`, `policy_id`, `action`, `chat_id`."},"value":{"type":"object","description":"Enrichment value (string/number/uuid)."},"type":{"type":"string","enum":["numeric","string","uid"]}},"required":["name","value","type"]},"V1EventDto":{"type":"object","properties":{"id":{"type":"string","description":"Stable event UUID (= analytics_prompts.id)."},"version":{"type":"string","description":"Aona webhook/event-payload version.","example":"2026-05-23"},"class_uid":{"type":"number","description":"OCSF class_uid. 6005 = Application Activity / Web Resources Activity.","example":6005},"category_name":{"type":"string","example":"Application Activity"},"activity_name":{"type":"string","example":"Access"},"time":{"type":"string","description":"Event timestamp (ISO-8601). = analytics_prompts.created_at.","format":"date-time"},"actor":{"$ref":"#/components/schemas/OcsfActorDto"},"app":{"$ref":"#/components/schemas/OcsfAppDto"},"type_name":{"type":"string","description":"Aona event class.","enum":["policy_violation","dlp_redaction","discovery"]},"disposition":{"type":"string","description":"Outcome of the policy evaluation. Aona-native values.","enum":["BLOCK","AUTO_REDACT","WARN","FLAG","NO_POLICY"]},"metadata":{"$ref":"#/components/schemas/OcsfMetadataDto"},"enrichments":{"type":"array","items":{"$ref":"#/components/schemas/OcsfEnrichmentDto"}}},"required":["id","version","class_uid","category_name","activity_name","time","actor","app","type_name","disposition","metadata","enrichments"]},"PaginatedEventsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1EventDto"}},"next_cursor":{"type":"string","description":"Cursor to pass as `?cursor=` on the next request, or null if this is the last page.","nullable":true},"has_more":{"type":"boolean","description":"True if `next_cursor` is non-null."}},"required":["data","next_cursor","has_more"]},"TestGuardrailBodyDto":{"type":"object","properties":{"input":{"type":"string","description":"The prompt or text to evaluate against the guardrail. Treated as a single user message.","example":"Hi, my name is John and my SSN is 123-45-6789."},"known_entities":{"type":"object","description":"Optional pre-known entities for redaction continuity. Map of entity-type → token values.","example":{"PERSON_1":["John"],"ORGANIZATION_1":["Aona"]}}},"required":["input"]},"UnevaluatedGuardrailDto":{"type":"object","properties":{"guardrail_id":{"type":"string","description":"Id of the guardrail that did not run."},"guardrail_name":{"type":"string","description":"Name of the guardrail that did not run."},"reason":{"type":"string","description":"Stable machine code for why it did not run. Values may be ADDED over time; treat an unknown code as \"not evaluated\" rather than failing.","enum":["no_active_version","no_endpoint","endpoint_error","no_entities_enabled","no_patterns","content_filter","no_input_extracted","no_applicable_guardrail"]},"detail":{"type":"string","description":"Human-readable explanation, safe to show an admin. Never contains endpoint URLs, credentials, or provider payloads.","nullable":true}},"required":["guardrail_id","guardrail_name","reason","detail"]},"BreachDto":{"type":"object","properties":{"guardrail_id":{"type":"string"},"version_id":{"type":"string","description":"Active version of the guardrail that produced this breach, when resolvable. Allows callers to deep-link to the exact version that was evaluated.","nullable":true},"guardrail_name":{"type":"string"},"action":{"type":"string","enum":["BLOCK","WARN","FLAG","AUTO_REDACT","NO_POLICY","COMPLIANT"]},"message":{"type":"string","nullable":true},"priority":{"type":"number","nullable":true}},"required":["guardrail_id","guardrail_name","action","message","priority"]},"RedactionEntryDto":{"type":"object","properties":{"search":{"type":"string","description":"Original substring matched in the input."},"replace":{"type":"string","description":"Placeholder Aona would substitute."},"type":{"type":"string","description":"Entity type / label (e.g. US_SSN, PERSON).","nullable":true}},"required":["search","replace","type"]},"EntityEntryDto":{"type":"object","properties":{"value":{"type":"string","description":"The detected text as it appears in the input."},"type":{"type":"string","description":"Entity type (e.g. CREDIT_CARD_PAN, PERSON, EMAIL_ADDRESS)."},"start":{"type":"number","description":"Start character offset in the input, when available.","nullable":true},"end":{"type":"number","description":"End character offset in the input, when available.","nullable":true},"action":{"type":"string","description":"Action configured on the guardrail that detected this entity.","enum":["BLOCK","WARN","FLAG","AUTO_REDACT","REDACT","MANUAL_REDACT","AUTO_UPDATE","MANUAL_UPDATE"]},"guardrail_id":{"type":"string","description":"The guardrail id that detected this entity."},"guardrail_name":{"type":"string","description":"The guardrail name that detected this entity."}},"required":["value","type","action","guardrail_id","guardrail_name"]},"TimingStepDto":{"type":"object","properties":{"key":{"type":"string","description":"Stable machine key for the step, e.g. \"guardrail_analysis\"."},"label":{"type":"string","description":"Human-readable label, e.g. \"Guardrail analysis\"."},"ms":{"type":"number","description":"Wall-clock time spent in this step, in milliseconds."}},"required":["key","label","ms"]},"GuardrailTimingDto":{"type":"object","properties":{"name":{"type":"string","description":"Guardrail name (falls back to its json key or category)."},"category":{"type":"string","description":"Guardrail category: llm, pii, regex, known_entities, external, or image_sensitive_info."},"ms":{"type":"number","description":"Time this guardrail took, in milliseconds. Guardrails run CONCURRENTLY, so these overlap and do NOT sum to the guardrail-analysis step; use them to see which guardrail dominates."},"chunk_count":{"type":"number","description":"Number of model calls this guardrail made. Enhanced entity-detection guardrails chunk long documents into many calls, which is often why one guardrail dominates the analysis time."},"chunked":{"type":"boolean","description":"True when this guardrail chunked the input into multiple calls."},"models":{"description":"Which Aona Orbital model(s) this guardrail used, in pipeline order (Orbital Radar / Orbital Lens / Orbital Compass / Orbital Reason / Orbital Vision). Present only for Orbital-backed guardrails; omitted for every other engine. Additive, optional.","type":"array","items":{"type":"string"}}},"required":["name","category","ms"]},"RequestTimingsDto":{"type":"object","properties":{"total_ms":{"type":"number","description":"Sum of the step durations below (server-side wall-clock), in milliseconds."},"steps":{"description":"Sequential server-side steps that make up the request time, in execution order, so callers can see where the latency went (database load, document parsing, guardrail analysis, etc.). Steps under a millisecond are omitted. Additive, optional field.","type":"array","items":{"$ref":"#/components/schemas/TimingStepDto"}},"guardrails":{"description":"Per-guardrail timings inside the guardrail-analysis step, slowest first. These run concurrently (they overlap and do not sum to the analysis total); use them to see which guardrail dominates. Additive, optional.","type":"array","items":{"$ref":"#/components/schemas/GuardrailTimingDto"}}},"required":["total_ms","steps"]},"GuardrailTestResponseDto":{"type":"object","properties":{"guardrail":{"type":"object"},"compliant":{"type":"boolean","description":"True only when every guardrail actually ran AND none produced a breach or a redaction. When `evaluated` is false this is ALWAYS false: a guardrail that did not run is not a guardrail that passed (ORB-92)."},"evaluated":{"type":"boolean","description":"False when at least one attached guardrail could not be evaluated (see `unevaluated`). Additive, optional field: older clients that ignore it see the same shape they always did, except that `compliant` can no longer come back true for a run that evaluated nothing."},"unevaluated":{"description":"Guardrails that were attached but never evaluated the input (no active version, no endpoint, provider unreachable, empty entity set, ...). Empty on the healthy path. Additive, optional field.","type":"array","items":{"$ref":"#/components/schemas/UnevaluatedGuardrailDto"}},"breaches":{"type":"array","items":{"$ref":"#/components/schemas/BreachDto"}},"redactions":{"type":"array","items":{"$ref":"#/components/schemas/RedactionEntryDto"}},"entities":{"description":"Every entity detected by any guardrail, with its per-entity action and originating guardrail. Superset of `redactions`: entities under FLAG/WARN/BLOCK actions appear here but NOT in `redactions`; redacted entities appear in both.","type":"array","items":{"$ref":"#/components/schemas/EntityEntryDto"}},"latency_ms":{"type":"number","description":"End-to-end evaluation time in milliseconds."},"timings":{"description":"Per-step breakdown of the server-side request time. Additive, optional.","allOf":[{"$ref":"#/components/schemas/RequestTimingsDto"}]}},"required":["guardrail","compliant","breaches","redactions","entities","latency_ms"]},"TestFrameworkBodyDto":{"type":"object","properties":{"input":{"type":"string","description":"The prompt or text to evaluate against the guardrail. Treated as a single user message.","example":"Hi, my name is John and my SSN is 123-45-6789."},"known_entities":{"type":"object","description":"Optional pre-known entities for redaction continuity. Map of entity-type → token values.","example":{"PERSON_1":["John"],"ORGANIZATION_1":["Aona"]}},"redaction_style":{"type":"string","description":"Optional per-request redaction style. `placeholder` replaces every redacted entity with the numbered `<TYPE_N>` placeholder; `realistic` replaces it with a same-type realistic substitute where one exists (types without one keep `<TYPE_N>`). Omit it, or send an unrecognized value, to keep the redaction style the framework's guardrails are configured for. Also accepted as the `X-Orbital-Redaction-Style` header; the body wins. Additive, optional field.","enum":["placeholder","realistic"],"example":"realistic"}},"required":["input"]},"FrameworkTestResponseDto":{"type":"object","properties":{"framework":{"type":"object"},"compliant":{"type":"boolean","description":"True only when every attached guardrail actually ran AND none produced a breach or a redaction. When `evaluated` is false this is ALWAYS false (ORB-92): a guardrail that did not run is not a guardrail that passed."},"evaluated":{"type":"boolean","description":"False when at least one attached guardrail could not be evaluated (see `unevaluated`). Additive, optional field."},"unevaluated":{"description":"Guardrails that were attached to this framework but never evaluated the input. Empty on the healthy path. Additive, optional field.","type":"array","items":{"$ref":"#/components/schemas/UnevaluatedGuardrailDto"}},"breaches":{"type":"array","items":{"$ref":"#/components/schemas/BreachDto"}},"redactions":{"type":"array","items":{"$ref":"#/components/schemas/RedactionEntryDto"}},"redaction_style":{"type":"string","description":"The redaction style this response was actually shaped with: `placeholder`, `realistic`, or null when the request carried none (or carried an unrecognized value) and the historical per-type shaping was used. Echoes what the server did, so a caller can tell a dropped or ignored `redaction_style` from an honoured one. Additive, optional field.","enum":["placeholder","realistic"],"nullable":true},"entities":{"description":"Every entity detected by any guardrail, with its per-entity action and originating guardrail. Superset of `redactions` (FLAG/WARN/BLOCK entities appear here but not in `redactions`).","type":"array","items":{"$ref":"#/components/schemas/EntityEntryDto"}},"latency_ms":{"type":"number"},"timings":{"description":"Per-step breakdown of the server-side request time. Additive, optional.","allOf":[{"$ref":"#/components/schemas/RequestTimingsDto"}]}},"required":["framework","compliant","breaches","redactions","entities","latency_ms"]},"RedactedFileDto":{"type":"object","properties":{"content":{"type":"string","description":"Base64-encoded content of the redacted file."},"filename":{"type":"string","description":"Suggested download filename, e.g. \"redacted_report.pdf\"."},"mimetype":{"type":"string","description":"MIME type of the redacted file."}},"required":["content","filename","mimetype"]},"DocumentSignatureDetectionDto":{"type":"object","properties":{"kind":{"type":"string","description":"Detector finding kind, for example visual_signature."},"page":{"type":"number","description":"Page number supplied by the detector, when applicable."},"bbox":{"description":"Detector bounding box [x1, y1, x2, y2], when available. Coordinates are metadata only; image crops and document bytes are never returned.","type":"array","items":{"type":"number"}},"confidence":{"type":"number"},"source":{"type":"string"}}},"DocumentSignatureEvidenceDto":{"type":"object","properties":{"evaluated":{"type":"boolean"},"detected":{"type":"boolean"},"complete":{"type":"boolean"},"count":{"type":"number"},"visual_count":{"type":"number"},"cryptographic_count":{"type":"number"},"pages":{"type":"array","items":{"type":"number"}},"kinds":{"type":"array","items":{"type":"string"}},"detections":{"type":"array","items":{"$ref":"#/components/schemas/DocumentSignatureDetectionDto"}},"warnings":{"type":"array","items":{"type":"string"}},"engine":{"type":"string","description":"Aona-owned detector identifier. This never exposes the underlying model architecture."}},"required":["evaluated","detected"]},"FrameworkFileTestResponseDto":{"type":"object","properties":{"framework":{"type":"object"},"file":{"type":"object"},"compliant":{"type":"boolean","description":"True only when every attached guardrail actually ran AND none produced a breach or a redaction. When `evaluated` is false this is ALWAYS false (ORB-92): a guardrail that did not run is not a guardrail that passed."},"evaluated":{"type":"boolean","description":"False when at least one attached guardrail could not be evaluated (see `unevaluated`). Additive, optional field."},"unevaluated":{"description":"Guardrails that were attached to this framework but never evaluated the uploaded file. Empty on the healthy path. Additive, optional field.","type":"array","items":{"$ref":"#/components/schemas/UnevaluatedGuardrailDto"}},"breaches":{"type":"array","items":{"$ref":"#/components/schemas/BreachDto"}},"redactions":{"type":"array","items":{"$ref":"#/components/schemas/RedactionEntryDto"}},"redaction_style":{"type":"string","description":"The redaction style this response was actually shaped with: `placeholder`, `realistic`, or null when the request carried none (or carried an unrecognized value) and the historical per-type shaping was used. Echoes what the server did, so a caller can tell a dropped or ignored `redaction_style` from an honoured one. Additive, optional field.","enum":["placeholder","realistic"],"nullable":true},"entities":{"description":"Every entity detected by any guardrail, with its per-entity action and originating guardrail. Superset of `redactions` (FLAG/WARN/BLOCK entities appear here but not in `redactions`).","type":"array","items":{"$ref":"#/components/schemas/EntityEntryDto"}},"parsed_preview":{"type":"string","description":"For document uploads (non-image), the first few hundred characters of the parsed text content. Useful to show admins what Aona \"saw\" inside the file.","nullable":true},"redacted_file":{"description":"For uploads with redactable entities, or a visual signature finding whose fired guardrail has a redacting action, a redacted copy of the file, base64-encoded. Signature copies are available for supported images and documents only after Orbital proves every verified region was applied. Null when there is nothing to redact, proof is incomplete, or the type is unsupported. Additive, optional field — older clients that ignore it are unaffected.\n\nWhen the request opts into deferral (`?defer_redacted_file=true`), this is omitted from the verdict for speed; fetch the redacted copy on demand via POST /v1/frameworks/:id/test-file/redacted instead.","nullable":true,"allOf":[{"$ref":"#/components/schemas/RedactedFileDto"}]},"redacted_file_available":{"type":"boolean","description":"True when this upload has text/entity redactions, or a fired visual signature guardrail with a redacting action. This can include supported images. Lets callers offer an on-demand download without paying the generation cost up front. Present regardless of deferral; especially useful when `redacted_file` was deferred. Additive, optional field."},"document_signatures":{"description":"Metadata-only evidence from Orbital Signature Vision. Present only when a managed signature guardrail evaluated a supported PNG, JPEG, PDF, or DOCX upload. Additive: older clients can ignore this field.","allOf":[{"$ref":"#/components/schemas/DocumentSignatureEvidenceDto"}]},"latency_ms":{"type":"number"},"timings":{"description":"Per-step breakdown of the server-side request time (framework load, document parsing, guardrail analysis, ...). Additive, optional.","allOf":[{"$ref":"#/components/schemas/RequestTimingsDto"}]}},"required":["framework","file","compliant","breaches","redactions","entities","latency_ms"]},"FrameworkFileRedactedResponseDto":{"type":"object","properties":{"framework":{"type":"object"},"file":{"type":"object"},"redacted_file":{"description":"The redacted copy of the upload, base64-encoded, generated on demand. Supported images can be returned for fired Signature Vision guardrails with redacting actions. Null when there are no redactions, signature application proof is incomplete, the type is unsupported, or an engine had to fall back to the original (an un-redacted original is never returned here).","nullable":true,"allOf":[{"$ref":"#/components/schemas/RedactedFileDto"}]},"redaction_style":{"type":"string","description":"The redaction style this response was actually shaped with: `placeholder`, `realistic`, or null when the request carried none (or carried an unrecognized value) and the historical per-type shaping was used. Echoes what the server did, so a caller can tell a dropped or ignored `redaction_style` from an honoured one. Additive, optional field.","enum":["placeholder","realistic"],"nullable":true},"timings":{"description":"Per-step breakdown of the server-side time to build the redacted copy (framework load, document parsing, guardrail analysis, redaction). This is where the redaction cost lives, e.g. the PDF redaction round-trip. Additive, optional.","allOf":[{"$ref":"#/components/schemas/RequestTimingsDto"}]}},"required":["framework","file"]},"V1UserDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"first_name":{"type":"string","nullable":true},"last_name":{"type":"string","nullable":true},"display_name":{"type":"string","nullable":true},"avatar_url":{"type":"string","nullable":true},"role":{"type":"string","enum":["admin","manager","employee","unknown"],"description":"Role within the business that issued the API key."},"status":{"type":"string","enum":["active","waiting_seat"],"description":"Membership status in the business."},"last_activity_at":{"type":"string","description":"Last recorded activity timestamp (ISO-8601).","nullable":true,"format":"date-time"},"created_at":{"type":"string","description":"ISO-8601 timestamp when the user joined the business.","format":"date-time"}},"required":["id","email","first_name","last_name","display_name","avatar_url","role","status","last_activity_at","created_at"]},"PaginatedUsersResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1UserDto"}},"next_cursor":{"type":"string","nullable":true},"has_more":{"type":"boolean"}},"required":["data","next_cursor","has_more"]},"OnboardingStatusDto":{"type":"object","properties":{"user_id":{"type":"string"},"onboarding_id":{"type":"string"},"completed":{"type":"boolean"}},"required":["user_id","onboarding_id","completed"]},"SetOnboardingBodyDto":{"type":"object","properties":{"completed":{"type":"boolean","description":"Desired completion state. `true` marks the user complete (idempotent); `false` resets it."}},"required":["completed"]},"UsageSummaryBucketDto":{"type":"object","properties":{"key":{"type":"string"},"total":{"type":"number"},"blocked":{"type":"number"},"auto_redacted":{"type":"number"},"warned":{"type":"number"},"flagged":{"type":"number"},"no_policy":{"type":"number"},"compliant":{"type":"number"}},"required":["key","total","blocked","auto_redacted","warned","flagged","no_policy","compliant"]},"UsageSummaryResponseDto":{"type":"object","properties":{"since":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"},"group_by":{"type":"string","enum":["tool","user","action","day"]},"buckets":{"type":"array","items":{"$ref":"#/components/schemas/UsageSummaryBucketDto"}}},"required":["since","until","group_by","buckets"]},"ViolationsSummaryBucketDto":{"type":"object","properties":{"key":{"type":"string"},"total":{"type":"number"},"critical":{"type":"number"},"high":{"type":"number"},"medium":{"type":"number"},"low":{"type":"number"}},"required":["key","total","critical","high","medium","low"]},"ViolationsSummaryResponseDto":{"type":"object","properties":{"since":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"},"group_by":{"type":"string","enum":["tool","user","severity","day"]},"buckets":{"type":"array","items":{"$ref":"#/components/schemas/ViolationsSummaryBucketDto"}}},"required":["since","until","group_by","buckets"]}}}}