openapi: 3.1.0 info: title: InvestMoz API version: 1.0.0 summary: Contrato consolidado da API do InvestMoz description: | Contrato consolidado para catálogos, oportunidades, manifestações de interesse, simulações, legislação, jobs, relatórios e administração. Valores de taxas, reduções e parâmetros de simulação são fornecidos pelo utilizador; não são taxas oficiais nem decisões de elegibilidade. contact: name: Equipa InvestMoz license: name: MIT identifier: MIT servers: - url: / description: Mesmo host da aplicação security: - sessionCookie: [] tags: - name: Catálogos description: Referências versionadas, estáveis e localizadas em pt-MZ. - name: Oportunidades description: Consulta pública de oportunidades de investimento. - name: Manifestações description: Submissão de interesse em oportunidades. - name: Simulações description: Execuções indicativas com snapshots e controlo de concorrência. - name: Jobs description: Estado dos trabalhos assíncronos associados a uma simulação. - name: Relatórios description: Leitura de snapshots imutáveis de relatórios. - name: Legislação description: Fontes legais publicadas e versionadas. - name: Administração description: Operações administrativas protegidas por sessão e autorização. paths: /api/v1/catalogs/{catalog}: get: tags: [Catálogos] operationId: listReferenceCatalog summary: Consultar catálogo versionado security: [] parameters: - $ref: '#/components/parameters/CatalogName' - $ref: '#/components/parameters/Version' - $ref: '#/components/parameters/CatalogType' - $ref: '#/components/parameters/ParentId' - $ref: '#/components/parameters/JurisdictionId' - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/PerPage' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Página do catálogo na versão solicitada headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/CatalogPage'} '404': {$ref: '#/components/responses/NotFound'} '422': {$ref: '#/components/responses/ValidationError'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/questionnaires/{code}: get: tags: [Simulações] operationId: getPublishedQuestionnaire summary: Consultar questionário publicado e versão imutável security: [] parameters: - $ref: '#/components/parameters/QuestionnaireCode' - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Questionário publicado em pt-MZ headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/QuestionnaireResponse'} '404': {$ref: '#/components/responses/NotFound'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/simulations: post: tags: [Simulações] operationId: createSimulation summary: Criar simulação rastreável description: Requer Idempotency-Key. Acesso anónimo requer token opaco gerado pelo cliente. x-implementation-status: implemented security: - sessionCookie: [] - {} parameters: - $ref: '#/components/parameters/IdempotencyKey' - $ref: '#/components/parameters/SimulationToken' - $ref: '#/components/parameters/CorrelationId' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/SimulationInput'} examples: BoundaryValues: summary: Exemplo técnico nos limites mínimos description: Valores mínimos para demonstrar a forma do payload; não são valores fiscais recomendados. value: {base: 0, rate: 0, reduction: 0} responses: '202': description: Simulação aceite para processamento headers: ETag: {$ref: '#/components/headers/ETag'} Location: {$ref: '#/components/headers/Location'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/SimulationAccepted'} '200': description: Repetição idempotente da resposta original headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/SimulationAccepted'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '409': {$ref: '#/components/responses/Conflict'} '422': {$ref: '#/components/responses/ValidationError'} '428': {$ref: '#/components/responses/PreconditionRequired'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/simulations/{id}: get: tags: [Simulações] operationId: getSimulation summary: Consultar simulação, cenários e estado de execução x-implementation-status: implemented security: - sessionCookie: [] - {} parameters: - $ref: '#/components/parameters/SimulationId' - $ref: '#/components/parameters/SimulationToken' - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Simulação dentro do âmbito autorizado headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/SimulationResponse'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '404': {$ref: '#/components/responses/NotFound'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/simulations/{id}/scenarios/{scenarioId}: patch: tags: [Simulações] operationId: updateSimulationScenario summary: Actualizar cenário com controlo optimista de concorrência x-implementation-status: implemented security: - sessionCookie: [] - {} parameters: - $ref: '#/components/parameters/SimulationId' - $ref: '#/components/parameters/ScenarioId' - $ref: '#/components/parameters/IfMatch' - $ref: '#/components/parameters/SimulationToken' - $ref: '#/components/parameters/CorrelationId' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/UpdateScenarioInput'} responses: '202': description: Alteração aceite e reexecução colocada em fila headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/Scenario'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '404': {$ref: '#/components/responses/NotFound'} '412': {$ref: '#/components/responses/PreconditionFailed'} '422': {$ref: '#/components/responses/ValidationError'} '428': {$ref: '#/components/responses/PreconditionRequired'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/simulations/{id}/jobs: get: tags: [Jobs] operationId: listSimulationJobs summary: Consultar jobs de execução e geração de relatórios description: Contrato reservado para consulta dos jobs associados à simulação. x-implementation-status: contract-only security: - sessionCookie: [] - {} parameters: - $ref: '#/components/parameters/SimulationId' - $ref: '#/components/parameters/SimulationToken' - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Jobs visíveis no âmbito autorizado headers: X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/JobCollection'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '404': {$ref: '#/components/responses/NotFound'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/simulations/{id}/reports/{reportId}: get: tags: [Relatórios] operationId: getSimulationReport summary: Obter snapshot imutável de relatório x-implementation-status: implemented security: - sessionCookie: [] - {} parameters: - $ref: '#/components/parameters/SimulationId' - $ref: '#/components/parameters/ReportId' - $ref: '#/components/parameters/SimulationToken' - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Snapshot e hash do relatório headers: X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/ReportResponse'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '404': {$ref: '#/components/responses/NotFound'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/legislation/sources: get: tags: [Legislação] operationId: listLegislationSources summary: Consultar fontes legais publicadas e versionadas description: Contrato reservado para catálogo público de fontes; administração usa a API v2 existente. x-implementation-status: contract-only security: [] parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/PerPage' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Fontes legais publicadas headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/LegislationPage'} '404': {$ref: '#/components/responses/NotFound'} '422': {$ref: '#/components/responses/ValidationError'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v2/investment/admin/legal-sources: get: tags: [Administração, Legislação] operationId: adminListLegalSources summary: Listar fontes legais para administração x-implementation-status: implemented security: [{sessionCookie: []}] parameters: - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Lista de fontes legais headers: X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/AdminLegalSourcePage'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} post: tags: [Administração, Legislação] operationId: adminCreateLegalSource summary: Registar fonte legal para validação x-implementation-status: implemented security: [{sessionCookie: []}] parameters: - $ref: '#/components/parameters/CorrelationId' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/LegalSourceInput'} responses: '201': description: Fonte legal registada headers: Location: {$ref: '#/components/headers/Location'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/LegalSource'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '409': {$ref: '#/components/responses/Conflict'} '422': {$ref: '#/components/responses/ValidationError'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v2/investment/admin/legal-sources/{id}/{action}: post: tags: [Administração, Legislação] operationId: adminTransitionLegalSource summary: Validar ou revogar uma fonte legal x-implementation-status: implemented security: [{sessionCookie: []}] parameters: - $ref: '#/components/parameters/SourceId' - name: action in: path required: true schema: {type: string, enum: [validate, revoke]} - $ref: '#/components/parameters/CorrelationId' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/LegalSourceTransitionInput'} responses: '200': description: Estado actualizado headers: X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/LegalSource'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '404': {$ref: '#/components/responses/NotFound'} '409': {$ref: '#/components/responses/Conflict'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/interest-requests: post: tags: [Manifestações] operationId: submitInterestRequest summary: Submeter manifestação de interesse x-implementation-status: implemented security: [] parameters: - $ref: '#/components/parameters/IdempotencyKeyOptional' - $ref: '#/components/parameters/CorrelationId' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/InterestRequestInput'} responses: '201': description: Manifestação submetida headers: X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/InterestRequestResponse'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '409': {$ref: '#/components/responses/Conflict'} '422': {$ref: '#/components/responses/ValidationError'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} /api/v1/opportunities/{slug}: get: tags: [Oportunidades] operationId: getOpportunity summary: Consultar oportunidade publicada x-implementation-status: implemented security: [] parameters: - name: slug in: path required: true schema: {type: string, minLength: 1, maxLength: 180} - $ref: '#/components/parameters/CorrelationId' responses: '200': description: Oportunidade publicada headers: X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/OpportunityResponse'} '404': {$ref: '#/components/responses/NotFound'} '429': {$ref: '#/components/responses/RateLimited'} '500': {$ref: '#/components/responses/ServerError'} components: securitySchemes: sessionCookie: type: apiKey in: cookie name: laravel-session description: Sessão Laravel; o nome pode ser substituído por SESSION_COOKIE. Rotas públicas e simulações anónimas substituem explicitamente este requisito. parameters: CatalogName: name: catalog in: path required: true schema: {type: string, enum: [authorities, jurisdictions, economic-activities]} Version: name: version in: query required: false description: Versão estável publicada do catálogo schema: {type: integer, minimum: 1} CatalogType: name: type in: query required: false description: Obrigatório para jurisdictions. schema: {type: string, enum: [country, province, district, admin_post, locality]} ParentId: name: parent_id in: query schema: {type: string, format: uuid} JurisdictionId: name: jurisdiction_id in: query schema: {type: string, format: uuid} Page: name: page in: query schema: {type: integer, minimum: 1, default: 1} PerPage: name: per_page in: query schema: {type: integer, minimum: 1, maximum: 100, default: 20} Search: name: q in: query schema: {type: string, maxLength: 100} QuestionnaireCode: name: code in: path required: true schema: {type: string, pattern: '^[a-z0-9][a-z0-9-]{0,79}$'} SimulationId: 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} SourceId: name: id in: path required: true schema: {type: string, format: uuid} IdempotencyKey: name: Idempotency-Key in: header required: true description: Repetições com a mesma chave e payload devolvem a resposta original; payload diferente produz conflito. schema: {type: string, minLength: 16, maxLength: 128} IdempotencyKeyOptional: name: Idempotency-Key in: header required: false description: Se fornecida, deve ser UUID; uma chave repetida com outro payload produz conflito. schema: {type: string, format: uuid} SimulationToken: name: X-Simulation-Token in: header required: false description: Obrigatório para acesso anónimo; token opaco do cliente, guardado apenas como hash. schema: {type: string, minLength: 32, maxLength: 256} IfMatch: name: If-Match in: header required: true description: ETag forte da representação lida previamente. schema: {type: string, minLength: 1, maxLength: 128} CorrelationId: name: X-Correlation-ID in: header required: false description: UUID opcional propagado pela aplicação; quando ausente, a aplicação cria um identificador. schema: {type: string, format: uuid} headers: ETag: description: Validador da representação; usado com If-Match em alterações concorrentes. schema: {type: string} Location: description: URI relativa do recurso criado ou aceite. schema: {type: string, format: uri-reference} RetryAfter: description: Segundos até à próxima tentativa. schema: {type: integer, minimum: 0} CorrelationId: description: Identificador de correlação da operação. schema: {type: string, format: uuid} responses: Unauthorized: description: Autenticação necessária ou sessão inválida headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: {application/json: {schema: {$ref: '#/components/schemas/Problem'}}} Forbidden: description: Actor autenticado sem autorização para a operação headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: {application/json: {schema: {$ref: '#/components/schemas/Problem'}}} NotFound: description: Recurso inexistente ou fora do âmbito visível headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: {application/json: {schema: {$ref: '#/components/schemas/Problem'}}} Conflict: description: Conflito de estado, versão ou chave idempotente headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: application/json: schema: {$ref: '#/components/schemas/Problem'} example: {message: A chave foi reutilizada com conteúdo diferente., code: idempotency_conflict} PreconditionFailed: description: If-Match não corresponde ao ETag actual headers: ETag: {$ref: '#/components/headers/ETag'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: application/json: schema: {$ref: '#/components/schemas/Problem'} example: {message: A representação mudou; volte a consultar o recurso., code: stale_etag} ValidationError: description: Dados inválidos headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: {application/json: {schema: {$ref: '#/components/schemas/ValidationProblem'}}} RateLimited: description: Limite de pedidos excedido headers: Retry-After: {$ref: '#/components/headers/RetryAfter'} X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'} content: {application/json: {schema: {$ref: '#/components/schemas/Problem'}}} ServerError: description: Erro interno; resposta não contém detalhes sensíveis headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: {application/json: {schema: {$ref: '#/components/schemas/Problem'}}} PreconditionRequired: description: Header obrigatório em falta, como If-Match ou Idempotency-Key headers: {X-Correlation-ID: {$ref: '#/components/headers/CorrelationId'}} content: {application/json: {schema: {$ref: '#/components/schemas/Problem'}}} schemas: Problem: type: object required: [message] properties: message: {type: string} code: {type: string} correlation_id: {type: string, format: uuid} additionalProperties: true ValidationProblem: allOf: - $ref: '#/components/schemas/Problem' - type: object properties: errors: type: object additionalProperties: type: array items: {type: string} CatalogItem: type: object required: [id, value, code, name, label, locale, version_no] properties: id: {type: string, format: uuid} value: {type: string} code: {type: string} name: {type: string, description: Rótulo apresentado em português de Moçambique.} label: {type: string} locale: {type: string, const: pt-MZ} parent_id: {type: [string, 'null']} type: {type: [string, 'null'], enum: [country, province, district, admin_post, locality, 'null']} authority_type: {type: [string, 'null']} organization_id: {type: [string, 'null'], format: uuid} jurisdiction_id: {type: [string, 'null'], format: uuid} jurisdiction_name: {type: [string, 'null']} source_code: {type: [string, 'null']} sector_id: {type: [string, 'null']} version_no: {type: integer} CatalogPage: type: object required: [data, meta] properties: data: {type: array, items: {$ref: '#/components/schemas/CatalogItem'}} links: type: object properties: next: {type: [string, 'null'], format: uri-reference} previous: {type: [string, 'null'], format: uri-reference} next_page_url: {type: [string, 'null'], format: uri-reference} prev_page_url: {type: [string, 'null'], format: uri-reference} current_page: {type: integer} per_page: {type: integer} total: {type: integer} last_page: {type: integer} meta: allOf: - $ref: '#/components/schemas/Pagination' - type: object properties: catalog: {type: string} version: {type: [integer, 'null']} locale: {type: string, const: pt-MZ} source_version: {type: [string, 'null']} status: {type: string, enum: [published, not_published]} Pagination: type: object required: [current_page, per_page, total] properties: current_page: {type: integer, minimum: 1} per_page: {type: integer, minimum: 1} total: {type: integer, minimum: 0} version: {type: string} QuestionnaireResponse: 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, additionalProperties: true} status: {type: string, const: published} valid_from: {type: [string, 'null'], format: date} valid_to: {type: [string, 'null'], format: date} SimulationInput: type: object required: [base, rate] properties: base: {type: number, minimum: 0, description: Valor de entrada sem moeda inferida.} rate: {type: number, minimum: 0, maximum: 100, description: Parâmetro fornecido pelo utilizador; não representa taxa oficial.} reduction: {type: number, minimum: 0, maximum: 100, default: 0} organization_id: {type: [string, 'null'], format: uuid} additionalProperties: false UpdateScenarioInput: type: object required: [label, base, rate] properties: label: {type: string, minLength: 1, maxLength: 100} base: {type: number, minimum: 0} rate: {type: number, minimum: 0, maximum: 100} reduction: {type: number, minimum: 0, maximum: 100, default: 0} additionalProperties: false SimulationAccepted: type: object required: [data, replayed] properties: data: {$ref: '#/components/schemas/Simulation'} replayed: {type: boolean} access_token: {type: string, minLength: 32, maxLength: 256, description: Devolvido apenas para execução anónima.} SimulationResponse: type: object required: [data] properties: data: {$ref: '#/components/schemas/Simulation'} Simulation: type: object required: [id, status, version_no, created_at, updated_at] properties: id: {type: string, format: uuid} questionnaire_version_id: {type: [string, 'null'], format: uuid} organization_id: {type: [string, 'null'], format: uuid} version_no: {type: integer, minimum: 1} status: {type: string, enum: [queued, running, completed, failed, cancelled]} input_snapshot: {type: object, additionalProperties: true} result_snapshot: {type: [object, 'null'], additionalProperties: true} calculation_snapshot: {type: [object, 'null'], additionalProperties: true} scenarios: {type: array, items: {$ref: '#/components/schemas/Scenario'}} created_at: {type: string, format: date-time} updated_at: {type: string, format: date-time} Scenario: type: object required: [id, code, label, version_no, parameters] properties: id: {type: string, format: uuid} code: {type: string} label: {type: string} version_no: {type: integer, minimum: 1} parameters: {type: object, additionalProperties: true} JobCollection: type: object required: [data] properties: data: {type: array, items: {$ref: '#/components/schemas/SimulationJob'}} SimulationJob: type: object required: [id, simulation_id, job_type, status, attempts, available_at] 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']} ReportResponse: type: object required: [data] properties: data: {$ref: '#/components/schemas/SimulationReport'} SimulationReport: type: object required: [id, simulation_id, version_no, status] 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'], additionalProperties: true} report_sha256: {type: [string, 'null'], pattern: '^[0-9a-f]{64}$'} LegislationPage: type: object required: [data, meta] properties: data: {type: array, items: {$ref: '#/components/schemas/LegalSource'}} meta: {$ref: '#/components/schemas/Pagination'} AdminLegalSourcePage: type: object required: [data] properties: data: {type: array, items: {$ref: '#/components/schemas/LegalSource'}} current_page: {type: integer} per_page: {type: integer} total: {type: integer} last_page: {type: integer} first_page_url: {type: [string, 'null'], format: uri-reference} last_page_url: {type: [string, 'null'], format: uri-reference} next_page_url: {type: [string, 'null'], format: uri-reference} prev_page_url: {type: [string, 'null'], format: uri-reference} LegalSourceInput: type: object required: [title, url, diploma, article, competent_entity, valid_from] properties: title: {type: string, minLength: 1, maxLength: 255} url: {type: string, format: uri, pattern: '^https://', maxLength: 2000} diploma: {type: string, maxLength: 255} article: {type: string, maxLength: 255} competent_entity: {type: string, maxLength: 255} valid_from: {type: string, format: date} valid_until: {type: [string, 'null'], format: date} additionalProperties: false LegalSourceTransitionInput: type: object required: [version_no] properties: version_no: {type: integer, minimum: 1} additionalProperties: false LegalSource: type: object properties: id: {type: string, format: uuid} title: {type: string} url: {type: string, format: uri} diploma: {type: string} article: {type: string} competent_entity: {type: string} validation_status: {type: string, enum: [pending, validated, revoked]} version_no: {type: integer} valid_until: {type: [string, 'null'], format: date} validator_id: {type: [integer, 'null']} validated_at: {type: [string, 'null'], format: date-time} valid_from: {type: [string, 'null'], format: date} InterestRequestInput: type: object required: [fullName, email, investorType, country, sectorInterest, message, privacyConsent] properties: fullName: {type: string, minLength: 3, maxLength: 150, writeOnly: true} email: {type: string, format: email, maxLength: 150, writeOnly: true} phone: {type: [string, 'null'], maxLength: 30, writeOnly: true} investorType: {type: string, enum: [individual, empresa, fundo, entidade_publica, outro]} country: {type: string, maxLength: 100} sectorInterest: {type: string, maxLength: 150} opportunityReference: {type: [string, 'null'], maxLength: 80} existingBusinessId: {type: [string, 'null'], format: uuid} modality: {type: [string, 'null'], maxLength: 50} investmentRange: {type: [string, 'null'], maxLength: 80} message: {type: string, minLength: 20, maxLength: 2000, writeOnly: true} privacyConsent: {type: boolean, const: true} shareWithProponent: {type: boolean, default: false} additionalProperties: false not: allOf: - required: [opportunityReference] properties: opportunityReference: {type: string, minLength: 1} - required: [existingBusinessId] properties: existingBusinessId: {type: string, format: uuid} allOf: - if: required: [existingBusinessId] properties: existingBusinessId: {type: string} then: required: [modality] properties: modality: {type: string, minLength: 1, maxLength: 50} InterestRequestResponse: type: object required: [success, message, data] properties: success: {type: boolean, const: true} message: {type: string} data: type: object required: [reference, status] properties: reference: {type: string} status: {type: string, const: received} OpportunityResponse: type: object required: [success, data] properties: success: {type: boolean, const: true} data: type: object required: [id, slug, title] properties: id: {type: [string, integer]} slug: {type: string} title: {type: string} summary: {type: [string, 'null']} status: {type: string}