RFC-0002: publish versioned public API reference - #1
Conversation
* docs: publish legacy batch integration guide * docs: build complete public API portal (#3)
…enapi # Conflicts: # .github/workflows/pages.yml # .github/workflows/required-ci.yml
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26f6b05613
ℹ️ 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".
|
Addressed the Codex review in 8da132c: aligned all examples with the canonical .mavula.dev owner contracts; corrected OAuth, payment, regulatory export, delivery and compliance payloads; added structured provenance and per-operation permission validation; replaced static redirects; and gated Pages publication behind guardian, contract and test checks with deploy-only permissions. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8da132cf9b
ℹ️ 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".
| const operationIds = []; | ||
| for (const file of ['identity-access', 'ledger-core', 'workbench']) { | ||
| const source = readFileSync(`openapi/${file}.public.v1.yaml`, 'utf8'); | ||
| operationIds.push(...[...source.matchAll(/^\s+operationId: (\S+)$/gm)].map((match) => match[1])); |
There was a problem hiding this comment.
Parse inline operationIds in uniqueness test
For the current Ledger contract, the six projection operations define operationId inline on the get: mapping, but this regex only matches IDs that appear on their own indented line. The test therefore counts 57 IDs while the contracts contain 63, so duplicate or missing IDs on those published projection routes would still pass CI and can break generated clients later.
Useful? React with 👍 / 👎.
|
|
||
| | Role | Typical responsibility | Core permissions | | ||
| | --- | --- | --- | | ||
| | `operations_viewer` | Read operational and financial state | `finance.read` | |
There was a problem hiding this comment.
Use role identifiers published by Identity
This table documents operations_viewer as a role to provision, and the same table later lists platform_administrator and service_integration, but the Identity /api/v1/me schema only publishes institution_admin, operations_maker, operations_checker, compliance_officer, and auditor as valid role values. Integrators following the guide can provision role IDs that generated clients reject or that the API never returns, so the table should align with the public enum or the contract should be updated.
Useful? React with 👍 / 👎.
| | State | Meaning | Client action | | ||
| | --- | --- | --- | | ||
| | Accepted or queued | Work is durable and awaiting execution | Poll with bounded backoff | | ||
| | Active | Provider interaction or reconciliation is in progress | Continue polling | |
There was a problem hiding this comment.
Document the WorkerJob status enum exactly
When clients poll GET /api/jobs/{jobId}, the response schema exposes WorkerJob.status as QUEUED, PROCESSING, COMPLETED, or FAILED, but this state table tells them to handle Accepted, Active, Expired, and Compensation required. Polling code written from this guide will branch on states the API does not return and may miss the real PROCESSING value, so the table should use the exact public enum or clearly map these labels to it.
Useful? React with 👍 / 👎.
| curl -sS -X POST https://workbench.mavula.dev/api/regulatory-exports/BATCH_ID/delivery \ | ||
| -H "Authorization: Bearer $COMPLIANCE_TOKEN" \ | ||
| -H 'Content-Type: application/json' \ | ||
| -H 'Idempotency-Key: 076a329b-84f5-4f58-99ef-04ac14f3a343' \ |
There was a problem hiding this comment.
Include the required delivery correlation header
The recordRegulatoryExportDelivery operation declares both Idempotency-Key and X-Correlation-ID as required headers, but this copy-paste delivery request only sends the idempotency header. Users following the guide for authority delivery will submit a request that fails request validation, so add the required X-Correlation-ID header here as in the export request above.
Useful? React with 👍 / 👎.
| ?response_type=code | ||
| &client_id=operator-console | ||
| &redirect_uri=https%3A%2F%2Fops.example.com%2Fcallback | ||
| &scope=openid%20profile%20finance.read%20finance.write |
There was a problem hiding this comment.
Request only published operator scopes
The authorization-code flow in the Identity contract publishes only openid, profile, and finance.read scopes, but this operator redirect asks for finance.write. Clients following this guide can receive an invalid_scope response before they ever reach the quickstart account creation flow, so either publish the write scope for this flow or remove it from the authorization request.
Useful? React with 👍 / 👎.
| source: 'contracts/openapi/workbench.public.v1.yaml', | ||
| }], | ||
| ]); | ||
| if (lock.version !== 1 || !Array.isArray(lock.contracts) || lock.contracts.length !== 3) { |
There was a problem hiding this comment.
Require each locked contract exactly once
After the new per-entry owner/source validation, the lock still only checks that there are three entries; a lock with identity-access.public.v1.yaml duplicated and workbench.public.v1.yaml omitted still passes every loop check, so the omitted contract’s digest is never verified. Track seen files or delete from expectedContracts and assert the expected set is exhausted so required CI cannot publish drift for an omitted owner contract.
Useful? React with 👍 / 👎.
| payload: | ||
| oneOf: | ||
| - $ref: '#/components/schemas/PaymentStartPayload' | ||
| - $ref: '#/components/schemas/PaymentSettlementPayload' | ||
| - $ref: '#/components/schemas/PaymentReconciliationPayload' |
There was a problem hiding this comment.
Tie job payload schemas to their job type
Because payload is validated independently from type, the public schema accepts mismatched jobs such as PAYMENT_CAPTURE with a settlement or reconciliation payload, even though those payloads cannot be executed as capture jobs. Generated clients and contract tests will treat those combinations as valid and then get runtime rejections, so model CreateJob as whole-object variants or conditional schemas keyed by the selected type.
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.
Add idempotency headers to job contract
This payment-job mutation is the only non-Identity POST without Idempotency-Key/X-Correlation-ID parameters, even though the docs tell integrators to use idempotency for mutating requests and the guide sends those headers. Generated clients built from the contract will omit the replay key for payment jobs, so a timeout retry can enqueue duplicate payment work instead of returning the original job receipt.
Useful? React with 👍 / 👎.
| link: /v1/getting-started/quickstart/ | ||
| icon: right-arrow | ||
| variant: primary | ||
| - text: Explore API reference | ||
| link: /v1/api/ledger-core/ |
There was a problem hiding this comment.
Prefix splash links with the Pages base path
With base: '/developer-docs', Astro does not automatically rewrite root-relative URLs in Markdown/frontmatter content, and the workflow links below already hardcode /developer-docs. These hero CTAs therefore point to https://mavulahq.github.io/v1/... on GitHub Pages instead of the deployed /developer-docs/v1/... routes, so clicking either primary action from the landing page lands on a 404.
Useful? React with 👍 / 👎.
| allOf: | ||
| - $ref: '#/components/schemas/CreateFinancialAdjustment' | ||
| - type: object | ||
| required: [id, status, requested_by, correlation_id, created_at] |
There was a problem hiding this comment.
Don't compose closed create schemas into responses
In OpenAPI 3.1/JSON Schema, the additionalProperties: false on CreateFinancialAdjustment still applies inside this allOf, so response fields added by the second schema such as id, status, and requested_by are treated as forbidden additional properties. The same closed-create-schema pattern appears on Rule, EntitySchema, and Workflow, making normal API responses fail validation in generated clients; move the shared properties into an open base schema or use unevaluatedProperties at the composed level.
Useful? React with 👍 / 👎.
Summary
Validation