docs: build complete MAVULA API portal - #3
Conversation
There was a problem hiding this comment.
💡 Codex Review
When POST /api/legacy-imports returns LegacyBatchCreated, it uses this LegacyBatchReceipt schema, but the schema allows direction: IMPORT while forcing every receipt's contract_id to the regulatory export contract. Valid import receipts will fail generated-client/schema validation, or clients will be unable to distinguish imports from exports by contract; allow the import contract id as well or split the receipt schemas.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| | Identity Access | `https://identity.mavula.io` | OAuth 2.0 and OpenID Connect | | ||
| | Ledger Core | `https://ledger.mavula.io` | Synchronous commands and reads | | ||
| | Workbench | `https://workbench.mavula.io` | Asynchronous jobs and operational reads | |
There was a problem hiding this comment.
Align guide hosts with the OpenAPI servers
For users copying the new v1 overview/examples, these base URLs send them to *.mavula.io, but the same commit's public OpenAPI contracts still advertise https://identity.mavula.dev, https://ledger.mavula.dev, and https://workbench.mavula.dev as their servers (openapi/*:12), so the portal points manual integrations/Postman at a different environment than Scalar/client generation. Please use one canonical host family in both the guides/collection and OpenAPI contracts.
Useful? React with 👍 / 👎.
| throw new Error('retry budget exhausted'); | ||
| ``` | ||
|
|
||
| See [Idempotency and retries](../concepts/idempotency/) for receipt and conflict semantics. |
There was a problem hiding this comment.
Fix the broken idempotency link
On the request-reliability page, this relative link resolves from /v1/getting-started/request-reliability/ to /v1/getting-started/concepts/idempotency/, but the page exists at /v1/concepts/idempotency/. Users clicking it from the published portal will hit a 404; change the target to ../../concepts/idempotency/.
Useful? React with 👍 / 👎.
| | `operations_viewer` | Read operational and financial state | `finance.read` | | ||
| | `operations_maker` | Submit controlled changes | `finance.read`, `finance.write` | | ||
| | `operations_checker` | Approve or reject controlled changes | `finance.read`, `finance.approve` | | ||
| | `compliance_officer` | Review and generate regulated outputs | `finance.read`, `compliance.read`, `compliance.write` | |
There was a problem hiding this comment.
Align role permissions with operation requirements
For users provisioning the documented minimum roles, the listed permissions do not satisfy the new x-mavula-permissions contract: operational reads in Workbench require workbench.read, and every legacy/regulatory operation requires compliance.manage, but this table grants only finance.read to operations_viewer and compliance.read/compliance.write to compliance_officer. Those roles will get 403s for the workflows they are documented to cover; update the table to use the actual permissions.
Useful? React with 👍 / 👎.
| -H 'Content-Type: application/json' \ | ||
| -H 'Idempotency-Key: 0e03ef93-09e0-413f-b49a-e59f93fa881f' \ | ||
| -H 'X-Correlation-ID: bd351e46-e436-4dd2-b9de-75e940629961' \ | ||
| -d '{"report_type":"TRANSACTION_ACTIVITY","period_start":"2026-06-01","period_end":"2026-06-30","format":"CSV"}' |
There was a problem hiding this comment.
Update regulatory export examples to the schema
When users copy this regulatory export command, Workbench rejects it because the new CreateRegulatoryExport schema has additionalProperties: false and requires period_from, period_to, legal_basis_code, and retention_until; this payload sends old report_type/period_start/period_end/format fields instead. The delivery example below has the same drift (authority_reference is required), so both examples need to match workbench.public.v1.yaml.
Useful? React with 👍 / 👎.
| -H 'Content-Type: application/json' \ | ||
| -H 'Idempotency-Key: bfe8b575-2137-4a86-a210-cc2182130aed' \ | ||
| -H 'X-Correlation-ID: fd662f62-19c7-4f92-941f-64667437e6f9' \ | ||
| -d '{"type":"PAYMENT_PROCESS","payload":{"tenant_id":"tenant_001","idempotency_key":"payment_001","correlation_id":"fd662f62-19c7-4f92-941f-64667437e6f9","rail":"BANK_TRANSFER","amount":"2500.00","payer":{"account_id":"account_001"},"payee":{"bank_account":"000123456789"}}}' |
There was a problem hiding this comment.
Replace the stale payment job payload
This payment example no longer matches the contract: CreateJob.type is restricted to PAYMENT_CAPTURE, PAYMENT_DISBURSEMENT, PAYMENT_SETTLEMENT, or PAYMENT_RECONCILIATION, while the sample sends PAYMENT_PROCESS, and the payload also uses BANK_TRANSFER, a decimal-string amount, and payer/payee shapes that do not match the schema. Copying the guide will produce a 400 instead of creating a job.
Useful? React with 👍 / 👎.
| "header": [ | ||
| { "key": "Authorization", "value": "Bearer {{access_token}}" }, | ||
| { "key": "Content-Type", "value": "application/json" }, | ||
| { "key": "Idempotency-Key", "value": "{{$guid}}" }, |
There was a problem hiding this comment.
Preserve idempotency keys across Postman retries
Postman dynamic variables such as {{$guid}} are generated when the request runs, so clicking Send again after a timeout creates a different Idempotency-Key for the same logical account/freeze command. That bypasses the retry receipt semantics documented elsewhere and can create duplicate mutations; store the key in a collection/environment variable instead of using {{$guid}} directly on mutating requests.
Useful? React with 👍 / 👎.
| --data-urlencode 'grant_type=client_credentials' \ | ||
| --data-urlencode 'client_id=YOUR_CLIENT_ID' \ | ||
| --data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \ | ||
| --data-urlencode 'scope=openid finance.read finance.write' |
There was a problem hiding this comment.
Expose finance scopes on service-token flow
The quickstart's client_credentials request asks for openid finance.read finance.write, but the OpenAPI clientCredentials flow only declares internal.worker as an allowed scope. Generated clients and Scalar auth for the service-token flow will not offer the finance scopes required by the subsequent Ledger Core calls, and an OAuth server following the contract can reject this token request; either add the service scopes to this flow or change the quickstart to a declared scope.
Useful? React with 👍 / 👎.
| allOf: | ||
| - $ref: '#/components/schemas/CreateFinancialAdjustment' | ||
| - type: object |
There was a problem hiding this comment.
Don’t compose closed request schemas into responses
For clients that validate responses against this contract, FinancialAdjustmentRequest can never validate a normal response: the referenced CreateFinancialAdjustment schema has additionalProperties: false, so response fields added by the second allOf branch such as id, status, and requested_by are rejected as additional properties. The same closed-schema extension pattern appears on Rule, EntitySchema, and Workflow; move the closure to the composed response schema or define full response schemas separately.
Useful? React with 👍 / 👎.
| description: Submit tenant-scoped payment work. The tenant is always derived from the access token. | ||
| tags: [Jobs] | ||
| x-mavula-permissions: [workbench.jobs.write] | ||
| requestBody: |
There was a problem hiding this comment.
Declare idempotency on job submission
Unlike the other mutating Workbench operations and the payment guide, POST /api/jobs has no Idempotency-Key or X-Correlation-ID parameters in the public contract. Generated clients from this OpenAPI file will omit the retry key when submitting payment jobs, so retrying after a lost response can enqueue duplicate work instead of returning the original job receipt.
Useful? React with 👍 / 👎.
* docs: publish legacy batch integration guide * docs: build complete public API portal (#3)
* feat: publish versioned public api reference * chore: sync licensed owner contracts * RFC-0002: publish legacy batch documentation (#2) * docs: publish legacy batch integration guide * docs: build complete public API portal (#3) * ci: harden Pages deployment permissions * fix: align portal guides with public contracts
Summary
Contract dependencies
Publication dependency
Validation