Files
Investidor-V2/docs/investmoz/simulations/openapi.json
T
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

1804 lines
78 KiB
JSON

{
"openapi": "3.1.0",
"info": {
"title": "InvestMoz Simulation Execution API",
"version": "1.0.0",
"description": "Indicative arithmetic scenarios. Rates and reductions are user supplied, not legal rates or eligibility decisions."
},
"servers": [{ "url": "/api/v1" }],
"paths": {
"/tax-parameters/resolve": {
"post": {
"summary": "Resolve published, source-validated tax parameters without calculating tax",
"description": "Returns only parameters whose published rule and validated source cover the requested period and whose declared conditions match. Missing parameters, context mismatches, overlaps and period changes remain indeterminate. CSRF applies because this read uses the web middleware/RLS context.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": [
"component",
"period_start",
"period_end"
],
"properties": {
"component": {
"type": "string",
"enum": [
"IRPC",
"IVA",
"ISPC",
"IRPS",
"INSS",
"RETENCOES",
"DIREITOS_ADUANEIROS",
"ICE"
]
},
"period_start": {
"type": "string",
"format": "date"
},
"period_end": {
"type": "string",
"format": "date"
},
"context": {
"type": "object",
"additionalProperties": {
"type": ["string", "null"]
}
}
}
}
}
}
},
"responses": {
"200": {
"description": "Resolved parameter records or explicit indeterminate states; no formula is evaluated",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"component",
"period",
"assumptions",
"parameters"
],
"properties": {
"component": { "type": "string" },
"period": {
"type": "object",
"properties": {
"start": {
"type": "string",
"format": "date"
},
"end": {
"type": "string",
"format": "date"
}
}
},
"assumptions": { "type": "object" },
"parameters": {
"type": "array",
"items": {
"type": "object",
"required": [
"parameter_code",
"status",
"value"
],
"properties": {
"parameter_code": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"resolved",
"indeterminate_missing",
"indeterminate_context",
"indeterminate_scope",
"indeterminate_overlap",
"indeterminate_period_change",
"indeterminate_invalid"
]
},
"value": {
"type": [
"string",
"null"
]
},
"unit": {
"type": "string"
},
"conditions": {
"type": "object"
},
"scope_key": {
"type": "string"
},
"rule_code": {
"type": "string"
},
"rule_version": {
"type": "integer"
},
"validity": {
"type": "object"
},
"source": {
"type": "object"
}
}
}
}
}
}
}
}
},
"422": {
"description": "Invalid period, component or context",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts": {
"post": {
"summary": "Create an encrypted, versioned simulator form draft",
"parameters": [
{ "$ref": "#/components/parameters/IdempotencyKey" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationFormDraft"
}
}
}
},
"responses": {
"201": {
"description": "Created with ETag",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftWriteResponseV1"
}
}
}
},
"200": {
"description": "Idempotent replay",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftWriteResponseV1"
}
}
}
},
"409": {
"description": "Draft exists or key reused with a different payload",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"422": {
"description": "Invalid draft field",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
},
"get": {
"summary": "List private projects owned by the signed-in account",
"description": "Requires an authenticated session. Only summary fields are returned.",
"responses": {
"200": {
"description": "Private draft summaries",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SimulationDraftSummaryV1"
}
}
}
}
}
}
},
"401": {
"description": "Authentication required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts/current": {
"get": {
"summary": "Read the current user's or anonymous token's draft",
"description": "A read does not extend the anonymous recovery period.",
"parameters": [
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"200": {
"description": "Draft snapshot and ETag; personal fields are decrypted only for its owner",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftReadResponseV1"
}
}
}
}
}
},
"delete": {
"summary": "Forget the current anonymous draft on this device",
"description": "Soft-deletes only the anonymous draft for the current recovery token and expires its recovery cookie. Account-owned projects are unaffected.",
"parameters": [
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"204": {
"description": "Recovery forgotten and cookie cleared"
},
"422": {
"description": "Anonymous recovery token required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts/claim-preview": {
"get": {
"summary": "Preview an anonymous draft for explicit account association",
"description": "Requires a signed-in account and the same browser recovery cookie; returns summary only and performs no transfer.",
"responses": {
"200": {
"description": "Candidate draft summary or null",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"oneOf": [
{
"$ref": "#/components/schemas/SimulationDraftSummaryV1"
},
{ "type": "null" }
]
}
}
}
}
}
}
}
}
},
"/simulation-drafts/claim": {
"post": {
"summary": "Explicitly continue an existing private project or claim as another project",
"description": "Requires a verified account. `continue_existing` opens the selected account draft and leaves the anonymous draft untouched. `save_as_new` atomically transfers the anonymous row into the account without merging or replacing any draft; replay is idempotent.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["choice"],
"properties": {
"choice": {
"type": "string",
"enum": [
"continue_existing",
"save_as_new"
]
},
"existing_draft_id": {
"type": "string",
"format": "uuid"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Selected/claimed private dossier and ETag",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftReadResponseV1"
}
}
}
},
"403": {
"description": "Verified account required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"404": {
"description": "Anonymous draft expired or selected dossier is not owned",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"409": {
"description": "Ownership conflict",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts/linkable-businesses": {
"get": {
"summary": "List the account owner's businesses eligible for an explicit private link",
"description": "Returns identifiers and labels only; no business data is copied to a project dossier.",
"responses": {
"200": {
"description": "Owned business choices",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"title",
"modality",
"status"
],
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"title": {
"type": "string"
},
"modality": {
"type": "string"
},
"status": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"/simulation-drafts/{id}/business-link": {
"get": {
"summary": "Read the active reversible link to an owned business",
"parameters": [{ "$ref": "#/components/parameters/Id" }],
"responses": {
"200": {
"description": "Active business reference or null"
},
"404": {
"description": "Dossier not owned",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
},
"post": {
"summary": "Explicitly link a private project to an owned business",
"description": "Stores a reference only. Does not copy data, alter the business, submit it, or publish it. The owner may unlink later.",
"parameters": [{ "$ref": "#/components/parameters/Id" }],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["business_id"],
"properties": {
"business_id": {
"type": "string",
"format": "uuid"
}
}
}
}
}
},
"responses": {
"201": { "description": "Link created" },
"200": { "description": "Idempotent replay" },
"403": {
"description": "Business not owned",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"409": {
"description": "Unlink the current business before changing the association",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
},
"delete": {
"summary": "Remove the active business association without deleting either record",
"parameters": [{ "$ref": "#/components/parameters/Id" }],
"responses": {
"200": {
"description": "Association ended; immutable link history retained"
},
"404": {
"description": "Dossier not owned",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts/{id}": {
"get": {
"summary": "Read an owned draft",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"200": {
"description": "Draft snapshot and ETag",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftReadResponseV1"
}
}
}
},
"404": {
"description": "Draft inaccessible",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
},
"put": {
"summary": "Autosave an owned draft with optimistic locking",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/IfMatch" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationFormDraft"
}
}
}
},
"responses": {
"200": {
"description": "Saved with new ETag and snapshot hash",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftWriteResponseV1"
}
}
}
},
"412": {
"description": "Stale draft version",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"422": {
"description": "Invalid draft field",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
},
"delete": {
"summary": "Soft-delete an owned draft",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/IfMatch" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"204": { "description": "Deleted" },
"412": {
"description": "Stale draft version",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts/{id}/touch": {
"post": {
"summary": "Renew anonymous recovery after an explicit continue-later action",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/IfMatch" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"200": {
"description": "Recovery deadline renewed without changing the snapshot version"
},
"412": {
"description": "Stale draft version",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulation-drafts/{id}/conversions": {
"post": {
"summary": "Attach a completed simulation to an investment draft with explicit consent",
"description": "The caller must own the draft and the simulation, or hold the matching anonymous token. The server requires an investment purpose, a completed execution and a ready report whose SHA-256 and canonical snapshot match the execution result. Repeating the same simulation attachment is idempotent. This operation only adds an immutable simulation reference to the draft; it does not submit a project or create a CRM record.",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/IfMatch" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AttachSimulationToDraftInput"
}
}
}
},
"responses": {
"200": {
"description": "Draft with the simulation reference and the replayed flag; returns the new ETag when attached",
"headers": {
"ETag": { "schema": { "type": "string" } },
"Cache-Control": {
"schema": {
"type": "string",
"const": "no-store, private"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationDraftAttachResponseV1"
}
}
}
},
"404": {
"description": "Draft or simulation is missing or outside the caller's authorized scope",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"409": {
"description": "Simulation is incomplete, has no ready report, or its report hash/content does not match the result",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"412": {
"description": "If-Match is stale",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"422": {
"description": "Consent is not granted or draft purpose is not an investment journey",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"428": {
"description": "If-Match is required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"429": {
"description": "Draft-write rate limit exceeded",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/questionnaires/{code}": {
"get": {
"summary": "Read the published Portuguese questionnaire version",
"parameters": [
{
"name": "code",
"in": "path",
"required": true,
"schema": { "type": "string" }
}
],
"responses": {
"200": {
"description": "Published questionnaire and stable version",
"headers": {
"ETag": { "schema": { "type": "string" } }
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QuestionnaireReadResponseV1"
}
}
}
},
"404": {
"description": "No published current version",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulations": {
"post": {
"summary": "Create a traceable simulation",
"parameters": [
{ "$ref": "#/components/parameters/IdempotencyKey" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationInput"
}
}
}
},
"responses": {
"202": {
"description": "Execution queued",
"headers": {
"ETag": { "schema": { "type": "string" } },
"Location": { "schema": { "type": "string" } }
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationCreateResponseV1"
}
}
}
},
"200": {
"description": "Idempotent replay",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationCreateResponseV1"
}
}
}
},
"409": {
"description": "Key reused with a different payload",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"422": {
"description": "Invalid input or missing anonymous token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"428": {
"description": "Idempotency-Key required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulations/history": {
"get": {
"summary": "List simulations visible to the verified account",
"description": "Returns summary metadata for the user's personal simulations and organization simulations visible through an active membership. Anonymous runs are excluded.",
"parameters": [
{
"name": "page",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"default": 1
}
},
{
"name": "per_page",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 50,
"default": 20
}
}
],
"responses": {
"200": {
"description": "Paginated authorized simulation history",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationHistoryResponseV1"
}
}
}
},
"401": {
"description": "Authentication required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"403": {
"description": "Verified email required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulations/{id}": {
"get": {
"summary": "Read a simulation and its snapshots",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"200": {
"description": "Simulation. Completed result snapshots preserve legacy fields and add contract_version, context and result; arithmetic status is not a fiscal conclusion.",
"headers": {
"ETag": { "schema": { "type": "string" } }
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationReadResponseV1"
}
}
}
},
"404": {
"description": "Missing or outside authorized scope",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulations/{id}/scenarios/{scenarioId}": {
"patch": {
"summary": "Update a scenario with optimistic concurrency",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/ScenarioId" },
{ "$ref": "#/components/parameters/IfMatch" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateScenarioInput"
}
}
}
},
"responses": {
"202": {
"description": "Updated; re-execution queued",
"headers": {
"ETag": { "schema": { "type": "string" } }
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationReadResponseV1"
}
}
}
},
"403": {
"description": "Edit not authorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"404": {
"description": "Simulation inaccessible",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"412": {
"description": "Stale ETag",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"428": {
"description": "If-Match required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
},
"422": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulations/{id}/reports/{reportId}": {
"get": {
"summary": "Read an immutable report snapshot",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/ReportId" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"200": {
"description": "Report snapshot and hash",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationReportReadResponseV1"
}
}
}
},
"404": {
"description": "Report inaccessible",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
},
"/simulations/{id}/jobs": {
"get": {
"summary": "Read non-sensitive execution job status",
"description": "Returns job type, retry state and a sanitized error code; worker exception details are never returned.",
"parameters": [
{ "$ref": "#/components/parameters/Id" },
{ "$ref": "#/components/parameters/SimulationToken" }
],
"responses": {
"200": {
"description": "Authorized job status list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SimulationJobsReadResponseV1"
}
}
}
},
"404": {
"description": "Simulation inaccessible",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorV1"
}
}
}
}
}
}
}
},
"components": {
"parameters": {
"Id": {
"name": "id",
"in": "path",
"required": true,
"schema": { "type": "string", "format": "uuid" }
},
"ScenarioId": {
"name": "scenarioId",
"in": "path",
"required": true,
"schema": { "type": "string", "format": "uuid" }
},
"ReportId": {
"name": "reportId",
"in": "path",
"required": true,
"schema": { "type": "string", "format": "uuid" }
},
"IdempotencyKey": {
"name": "Idempotency-Key",
"in": "header",
"required": true,
"schema": {
"type": "string",
"minLength": 16,
"maxLength": 128
}
},
"SimulationToken": {
"name": "X-Simulation-Token",
"in": "header",
"required": false,
"description": "Legacy/API-client fallback for anonymous access. Browsers use the HttpOnly, SameSite=Lax investmoz_simulation_recovery cookie; only an HMAC hash is stored.",
"schema": {
"type": "string",
"minLength": 32,
"maxLength": 256
}
},
"IfMatch": {
"name": "If-Match",
"in": "header",
"required": true,
"description": "Strong ETag; absent returns 428, stale returns 412.",
"schema": { "type": "string" }
}
},
"schemas": {
"ApiErrorV1": {
"type": "object",
"properties": {
"message": { "type": "string" },
"errors": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": { "type": "string" }
}
}
},
"additionalProperties": true
},
"QuestionnaireReadResponseV1": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"type": "object",
"required": [
"id",
"code",
"version_no",
"locale",
"schema_snapshot",
"status"
],
"properties": {
"id": { "type": "string", "format": "uuid" },
"code": { "type": "string" },
"version_no": { "type": "integer", "minimum": 1 },
"locale": { "type": "string", "const": "pt-MZ" },
"schema_snapshot": { "type": "object" },
"status": {
"type": "string",
"const": "published"
},
"valid_from": {
"type": ["string", "null"],
"format": "date"
},
"valid_to": {
"type": ["string", "null"],
"format": "date"
}
},
"additionalProperties": true
}
},
"additionalProperties": true
},
"SimulationHistoryResponseV1": {
"type": "object",
"required": ["data", "meta"],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"scope",
"organization_name",
"status",
"version_no",
"created_at",
"updated_at",
"has_ready_report"
],
"properties": {
"id": { "type": "string", "format": "uuid" },
"scope": {
"type": "string",
"enum": ["personal", "organization"]
},
"organization_name": {
"type": ["string", "null"]
},
"status": {
"type": "string",
"enum": [
"queued",
"running",
"completed",
"failed",
"cancelled"
]
},
"version_no": {
"type": "integer",
"minimum": 1
},
"created_at": {
"type": ["string", "null"],
"format": "date-time"
},
"updated_at": {
"type": ["string", "null"],
"format": "date-time"
},
"has_ready_report": { "type": "boolean" }
}
}
},
"meta": {
"type": "object",
"required": [
"current_page",
"last_page",
"per_page",
"total"
],
"properties": {
"current_page": { "type": "integer", "minimum": 1 },
"last_page": { "type": "integer", "minimum": 1 },
"per_page": {
"type": "integer",
"minimum": 1,
"maximum": 50
},
"total": { "type": "integer", "minimum": 0 }
}
}
},
"additionalProperties": true
},
"SimulationSourceV1": {
"type": "object",
"required": [
"simulation_id",
"source_version_no",
"result_snapshot",
"report_sha256",
"consented_at"
],
"properties": {
"simulation_id": { "type": "string", "format": "uuid" },
"source_version_no": { "type": "integer", "minimum": 1 },
"result_snapshot": { "type": "object" },
"report_sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"consented_at": { "type": "string", "format": "date-time" }
},
"additionalProperties": true
},
"SimulationDraftSummaryV1": {
"type": "object",
"required": [
"id",
"title",
"version_no",
"updated_at",
"expires_at"
],
"properties": {
"id": { "type": "string", "format": "uuid" },
"title": { "type": "string" },
"version_no": { "type": "integer", "minimum": 1 },
"updated_at": {
"type": ["string", "null"],
"format": "date-time"
},
"expires_at": {
"type": ["string", "null"],
"format": "date-time"
}
}
},
"SimulationDraftV1": {
"type": "object",
"required": [
"id",
"version_no",
"snapshot_sha256",
"personal_snapshot",
"economic_snapshot",
"simulation_sources",
"updated_at",
"expires_at"
],
"properties": {
"id": { "type": "string", "format": "uuid" },
"version_no": { "type": "integer", "minimum": 1 },
"snapshot_sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"personal_snapshot": {
"oneOf": [
{ "type": "object" },
{ "type": "array", "maxItems": 0 }
],
"description": "Anonymous drafts return an empty array; account drafts may contain allowlisted private fields."
},
"economic_snapshot": { "type": "object" },
"simulation_sources": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SimulationSourceV1"
}
},
"updated_at": {
"type": ["string", "null"],
"format": "date-time"
},
"expires_at": {
"type": ["string", "null"],
"format": "date-time",
"description": "Anonymous draft deadline; extended by save or recovery access. Account-owned drafts do not expire."
}
},
"additionalProperties": true
},
"SimulationDraftReadResponseV1": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"oneOf": [
{
"$ref": "#/components/schemas/SimulationDraftV1"
},
{ "type": "null" }
]
}
},
"additionalProperties": true
},
"SimulationDraftWriteResponseV1": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"type": "object",
"required": ["id", "version_no"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"version_no": { "type": "integer", "minimum": 1 },
"replayed": { "type": "boolean" }
}
},
"expires_at": {
"type": ["string", "null"],
"format": "date-time"
}
},
"additionalProperties": true
},
"SimulationDraftAttachResponseV1": {
"type": "object",
"required": ["data", "replayed"],
"properties": {
"data": {
"$ref": "#/components/schemas/SimulationDraftV1"
},
"replayed": { "type": "boolean" }
},
"additionalProperties": true
},
"SimulationReportReadResponseV1": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"type": "object",
"required": [
"id",
"simulation_id",
"version_no",
"status",
"report_snapshot",
"report_sha256"
],
"properties": {
"id": { "type": "string", "format": "uuid" },
"simulation_id": {
"type": "string",
"format": "uuid"
},
"version_no": { "type": "integer", "minimum": 1 },
"status": {
"type": "string",
"enum": ["queued", "ready", "failed"]
},
"report_snapshot": { "type": ["object", "null"] },
"report_sha256": {
"type": ["string", "null"],
"pattern": "^[a-f0-9]{64}$"
}
},
"additionalProperties": true
}
},
"additionalProperties": true
},
"SimulationJobsReadResponseV1": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"simulation_id",
"job_type",
"status",
"attempts",
"available_at",
"started_at",
"finished_at",
"error_code"
],
"properties": {
"id": { "type": "string", "format": "uuid" },
"simulation_id": {
"type": "string",
"format": "uuid"
},
"job_type": {
"type": "string",
"enum": ["execute", "report"]
},
"status": {
"type": "string",
"enum": [
"pending",
"running",
"succeeded",
"failed"
]
},
"attempts": {
"type": "integer",
"minimum": 0,
"maximum": 3
},
"available_at": {
"type": "string",
"format": "date-time"
},
"started_at": {
"type": ["string", "null"],
"format": "date-time"
},
"finished_at": {
"type": ["string", "null"],
"format": "date-time"
},
"error_code": { "type": ["string", "null"] }
},
"additionalProperties": true
}
}
},
"additionalProperties": true
},
"DecimalInputString": {
"type": "string",
"pattern": "^(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$",
"description": "Non-negative exact decimal encoded as a JSON string; numeric ranges are enforced by the API."
},
"PercentageInputString": {
"type": "string",
"pattern": "^(?:0|[1-9][0-9]?)(?:\\.[0-9]+)?$|^100(?:\\.0+)?$",
"description": "Exact decimal percentage encoded as a JSON string from 0 through 100; the API enforces the numeric range."
},
"SimulationInput": {
"type": "object",
"additionalProperties": false,
"required": ["base", "rate", "reduction"],
"properties": {
"base": {
"allOf": [
{
"$ref": "#/components/schemas/DecimalInputString"
}
],
"description": "User input; no currency is inferred."
},
"rate": {
"allOf": [
{
"$ref": "#/components/schemas/PercentageInputString"
}
],
"description": "User scenario rate; not an official rate."
},
"reduction": {
"allOf": [
{
"$ref": "#/components/schemas/PercentageInputString"
}
],
"description": "User supplied scenario reduction; no default is applied."
},
"organization_id": {
"type": "string",
"format": "uuid",
"description": "Optional; requires admin, manager or officer membership."
}
}
},
"AttachSimulationToDraftInput": {
"type": "object",
"additionalProperties": false,
"required": ["simulation_id", "consent_reuse"],
"properties": {
"simulation_id": { "type": "string", "format": "uuid" },
"consent_reuse": {
"type": "boolean",
"const": true,
"description": "Explicit consent is required; false is rejected."
}
}
},
"SimulationFormDraft": {
"type": "object",
"additionalProperties": false,
"required": ["personal_snapshot", "economic_snapshot"],
"properties": {
"personal_snapshot": {
"type": "object",
"description": "Allowlisted personal fields, encrypted at rest; only owner/token can read."
},
"economic_snapshot": {
"type": "object",
"required": ["purpose"],
"description": "Fiscal rates are server-selected from approved effective versions; user-entered assumptions are confined to custom_planning.",
"properties": {
"fiscal_scenario_mode": {
"type": "string",
"enum": [
"approved_parameters",
"custom_planning",
"no_calculation"
]
},
"business_plan_snapshot": {
"type": "string",
"maxLength": 60000,
"description": "Versioned business financial plan JSON snapshot (v1-v16); monthly purchase, supplier installment payment, expense, payroll and per-product inventory schedules are optional."
},
"financial_tools_snapshot": {
"type": "string",
"maxLength": 30000,
"description": "Versioned financial calculator assumptions JSON snapshot."
}
}
}
}
},
"UpdateScenarioInput": {
"type": "object",
"additionalProperties": false,
"required": ["label", "base", "rate", "reduction"],
"properties": {
"label": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"base": {
"allOf": [
{
"$ref": "#/components/schemas/DecimalInputString"
}
]
},
"rate": {
"allOf": [
{
"$ref": "#/components/schemas/PercentageInputString"
}
]
},
"reduction": {
"allOf": [
{
"$ref": "#/components/schemas/PercentageInputString"
}
],
"description": "User supplied scenario reduction; no default is applied."
}
}
},
"SimulationExecutionV1": {
"type": "object",
"required": ["id", "status", "version_no", "result_snapshot"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"status": {
"type": "string",
"enum": [
"queued",
"running",
"completed",
"failed",
"cancelled"
]
},
"version_no": { "type": "integer", "minimum": 1 },
"result_snapshot": {
"type": ["object", "null"],
"description": "Immutable calculation snapshot when available; null before completion."
}
},
"additionalProperties": true
},
"SimulationCreateResponseV1": {
"type": "object",
"required": ["data", "replayed"],
"properties": {
"data": {
"$ref": "#/components/schemas/SimulationExecutionV1"
},
"replayed": { "type": "boolean" },
"access_token": {
"type": "string",
"minLength": 32,
"maxLength": 256,
"description": "Returned only for an anonymous execution."
}
},
"additionalProperties": true
},
"SimulationReadResponseV1": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"$ref": "#/components/schemas/SimulationExecutionV1"
}
},
"additionalProperties": true
},
"SimulationResult": {
"type": "object",
"required": [
"status",
"value",
"unit",
"explanation",
"missing_inputs",
"source_ids"
],
"properties": {
"status": {
"type": "string",
"enum": [
"calculated",
"estimated",
"indeterminate",
"not_applicable"
]
},
"value": {
"type": ["string", "null"],
"pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$",
"description": "Exact decimal string; null for indeterminate or not_applicable."
},
"unit": { "type": ["string", "null"] },
"explanation": { "type": "string", "minLength": 1 },
"missing_inputs": {
"type": "array",
"items": { "type": "string" }
},
"source_ids": {
"type": "array",
"items": { "type": "string" }
}
}
},
"SimulationResultContext": {
"type": "object",
"required": [
"reference_date",
"currency",
"currency_fraction_digits",
"scenario_id",
"scenario_version",
"engine_version",
"ruleset_version"
],
"properties": {
"reference_date": { "type": "string", "format": "date" },
"currency": { "type": ["string", "null"] },
"currency_fraction_digits": {
"type": ["integer", "null"],
"minimum": 0
},
"scenario_id": { "type": "string", "format": "uuid" },
"scenario_version": { "type": "integer", "minimum": 1 },
"engine_version": { "type": "string" },
"ruleset_version": { "type": ["string", "null"] }
}
},
"SimulationResultSnapshotV1": {
"type": "object",
"required": [
"contract_version",
"context",
"result",
"indicative_total",
"legal_interpretation",
"formula_version"
],
"properties": {
"contract_version": { "type": "integer", "const": 1 },
"context": {
"$ref": "#/components/schemas/SimulationResultContext"
},
"result": {
"$ref": "#/components/schemas/SimulationResult"
},
"indicative_total": {
"type": ["string", "null"],
"pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$",
"description": "Legacy decimal when the result has a value; null for indeterminate or not-applicable states."
},
"legal_interpretation": {
"type": "boolean",
"const": false
},
"formula_version": { "type": "string" }
}
}
}
}
}