From ba1ed7f66a9e54a854bb7fd9795c6bec80c4b0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Mon, 4 May 2026 13:11:37 -0300 Subject: [PATCH 1/5] docs(api): expose tags on account and operation responses --- apis/fx-account/openapi.yml | 22 ++++++++++++++++++++++ apis/fx-payment/openapi.yml | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/apis/fx-account/openapi.yml b/apis/fx-account/openapi.yml index cf5a298..f4084bd 100644 --- a/apis/fx-account/openapi.yml +++ b/apis/fx-account/openapi.yml @@ -359,8 +359,18 @@ components: $ref: "#/components/schemas/RequirementsResponse" tags: type: array + description: > + System-managed labels attached to the account. Some tags + propagate to operations executed against this account. items: $ref: "#/components/schemas/TagResponse" + example: + - key: "psp" + value: "Amazon" + - key: null + value: "hidden" + - key: "compliance-tier" + value: "high" currentState: $ref: "#/components/schemas/AccountState" states: @@ -1160,12 +1170,24 @@ components: - AX TagResponse: type: object + description: > + A label attached to a resource. Tags are system-managed — Trace + assigns them based on routing, compliance, and operational criteria. + Clients cannot create, modify, or remove tags via the API. properties: key: type: string nullable: true + description: > + Category for keyed tags (e.g., `psp`, `compliance-tier`). + `null` for single-value labels. + example: "psp" value: type: string + description: > + For keyed tags, the right-hand side of the label. For + single-value labels, the entire tag. + example: "Amazon" required: - value UBOResponse: diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index 7da51e2..b74ba44 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -229,6 +229,28 @@ components: required: - id - owner + TagResponse: + type: object + description: > + A label attached to a resource. Tags are system-managed — Trace + assigns them based on routing, compliance, and operational criteria. + Clients cannot create, modify, or remove tags via the API. + properties: + key: + type: string + nullable: true + description: > + Category for keyed tags (e.g., `psp`, `compliance-tier`). + `null` for single-value labels. + example: "psp" + value: + type: string + description: > + For keyed tags, the right-hand side of the label. For + single-value labels, the entire tag. + example: "Amazon" + required: + - value Reason: type: object description: Machine- and human-readable explanation. Returned inside an `OperationState` entry when a status transition requires justification (e.g., `FAILED`, `ON_HOLD`). @@ -1009,6 +1031,18 @@ components: type: array items: $ref: "#/components/schemas/OperationTransaction" + tags: + type: array + description: > + System-managed labels stamped onto the operation when it is + created. Inherited from the account's propagating tags. + items: + $ref: "#/components/schemas/TagResponse" + example: + - key: "psp" + value: "Amazon" + - key: "compliance-tier" + value: "high" currentState: $ref: "#/components/schemas/OperationState" states: @@ -1034,6 +1068,7 @@ components: - quote - fees - transactions + - tags - currentState - states - createdAt From 9ebf45fcc74dbaea8703ce80bf1c276814547d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Mon, 4 May 2026 18:35:02 -0300 Subject: [PATCH 2/5] docs(api): add reports endpoint --- .../fx-payment/reports/get-report.mdx | 5 + apis/fx-payment/openapi.yml | 227 +++++++++++++++++- docs.json | 7 + 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 api-reference/fx-payment/reports/get-report.mdx diff --git a/api-reference/fx-payment/reports/get-report.mdx b/api-reference/fx-payment/reports/get-report.mdx new file mode 100644 index 0000000..e24faab --- /dev/null +++ b/api-reference/fx-payment/reports/get-report.mdx @@ -0,0 +1,5 @@ +--- +title: "Get a report" +description: "Aggregated view of operations over a time window, partitioned by asset." +openapi: "apis/fx-payment/openapi.yml GET /api/reports" +--- diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index b74ba44..01e5e87 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -15,6 +15,8 @@ security: tags: - name: Operations description: Create deposits, withdrawals, and swaps. Query operation status and history. + - name: Reports + description: Generate aggregated views of operations across a time window. - name: Beneficiaries description: Manage external beneficiaries and their payment instructions for withdrawals. - name: Payment instructions @@ -118,10 +120,35 @@ components: description: > Filter expression using LHS Brackets syntax (`field[operator]=value`). Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). - See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. + See the [Filtering](/guides/principles/filtering) guide for the full operator catalog. schema: type: string example: "intent.type[eq]=WITHDRAWAL;currentState.status[eq]=COMPLETED" + PeriodStart: + name: periodStart + in: query + required: true + description: > + Inclusive start of the reporting window. UTC timestamp in ISO 8601 format — + the `Z` suffix is the timezone marker and is required (e.g., `2026-04-01T00:00:00Z`). + See the [Date and time](/guides/principles/datetime) guide for the full format. + schema: + type: string + format: date-time + example: "2026-04-01T00:00:00Z" + PeriodEnd: + name: periodEnd + in: query + required: true + description: > + Exclusive end of the reporting window. UTC timestamp in ISO 8601 format — + the `Z` suffix is the timezone marker and is required (e.g., `2026-05-01T00:00:00Z`). + Must be strictly greater than `periodStart`. + See the [Date and time](/guides/principles/datetime) guide for the full format. + schema: + type: string + format: date-time + example: "2026-05-01T00:00:00Z" schemas: # ── Shared value objects ────────────────────────────────────────── Asset: @@ -726,6 +753,66 @@ components: - code - message + # ── Report schemas ──────────────────────────────────────────────── + # Documented here ahead of the Report domain model and controller. + ReportPeriod: + type: object + description: The reporting window. Boundary semantics are half-open — `start` inclusive, `end` exclusive. + properties: + start: + type: string + format: date-time + description: Inclusive start of the reporting window. + example: "2026-04-01T00:00:00Z" + end: + type: string + format: date-time + description: Exclusive end of the reporting window. + example: "2026-05-01T00:00:00Z" + required: + - start + - end + ReportAggregate: + type: object + description: Sum and count of monetary movements on a single side of the ledger within an asset partition. + properties: + sum: + $ref: "#/components/schemas/AmountResponse" + count: + type: integer + minimum: 0 + description: Number of contributing transactions. + example: 3 + required: + - sum + - count + ReportTotal: + type: object + description: Inflows and outflows for one asset over the reporting window. A row is present only when at least one settled transaction in that asset matched the filter. + properties: + asset: + $ref: "#/components/schemas/Asset" + inflows: + $ref: "#/components/schemas/ReportAggregate" + outflows: + $ref: "#/components/schemas/ReportAggregate" + required: + - asset + ReportResponse: + type: object + description: Aggregated view of operations over a time window. Generated on each request — not persisted. + properties: + period: + $ref: "#/components/schemas/ReportPeriod" + totals: + type: array + description: One entry per asset that had at least one settled transaction matching the filter in the period. Empty when nothing matched. + items: + $ref: "#/components/schemas/ReportTotal" + required: + - period + - totals + # ── Quote schemas ───────────────────────────────────────────────── CreateQuoteRequest: type: object @@ -2199,6 +2286,85 @@ components: message: "Parameter 'body:accountId' not found in request" field: "body:accountId" params: {} + ReportMultiAssetActivity: + summary: Mixed deposits and withdrawals across BRL and USDT + value: + period: + start: "2026-04-01T00:00:00Z" + end: "2026-05-01T00:00:00Z" + totals: + - asset: "BRL" + inflows: + sum: + value: "5000.00" + asset: "BRL" + decimals: 2 + count: 2 + outflows: + sum: + value: "10000.00" + asset: "BRL" + decimals: 2 + count: 2 + - asset: "USDT" + outflows: + sum: + value: "2000.000000" + asset: "USDT" + decimals: 6 + count: 2 + ReportWithdrawalsOnly: + summary: Filtered to withdrawals only — no inflows + value: + period: + start: "2026-04-01T00:00:00Z" + end: "2026-05-01T00:00:00Z" + totals: + - asset: "BRL" + outflows: + sum: + value: "10000.00" + asset: "BRL" + decimals: 2 + count: 2 + - asset: "USDT" + outflows: + sum: + value: "2000.000000" + asset: "USDT" + decimals: 6 + count: 2 + ReportEmptyPeriod: + summary: No settled transactions matched the filter in the period + value: + period: + start: "2026-04-01T00:00:00Z" + end: "2026-05-01T00:00:00Z" + totals: [] + ReportMissingPeriodStart: + summary: periodStart is missing from the query string + value: + code: "INVALID_DATA" + message: "Object contains invalid data" + details: + errors: + - code: "REQUIRED" + message: "Parameter 'query:periodStart' not found in request" + field: "query:periodStart" + params: {} + ReportPeriodEndNotAfterStart: + summary: periodEnd must be strictly greater than periodStart + value: + code: "INVALID_DATA" + message: "Object contains invalid data" + details: + errors: + - code: "INVALID_RANGE" + message: "'periodEnd' must be greater than 'periodStart'" + field: "query:periodEnd" + params: + periodStart: "2026-05-01T00:00:00Z" + periodEnd: "2026-04-01T00:00:00Z" paths: # ── Operations: Withdrawals ─────────────────────────────────────── @@ -3049,6 +3215,65 @@ paths: field: "query:status" params: {} + # ── Reports ─────────────────────────────────────────────────────── + /api/reports: + get: + operationId: getReport + tags: + - Reports + summary: Get a report + description: | + Generates an aggregated view of operations over a time window. Activity is partitioned by `asset` because amounts in different currencies cannot be summed. Only settled transactions (`CONFIRMED` status) on operations matching `filters` contribute — pending and failed are excluded. The report is regenerated on each request and is not persisted. + parameters: + - $ref: "#/components/parameters/TraceVersion" + - $ref: "#/components/parameters/PeriodStart" + - $ref: "#/components/parameters/PeriodEnd" + - $ref: "#/components/parameters/Filters" + description: | + Filter expression using LHS Brackets syntax (`field[operator]=value`). Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). + + Common filters for reports: + + - `account.id[eq]=a1b2c3d4-5e6f-7890-abcd-ef1234567890` — single account + - `asset[eq]=USD` — specific currency + - `tags.key[eq]=psp` — operations carrying a tag with this key + - `tags.value[eq]=Amazon` — operations carrying a tag with this value + + See the [Filtering](/guides/principles/filtering) guide for the full operator catalog. + responses: + "200": + description: Aggregated report for the requested window. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ReportResponse" + examples: + multiAssetActivity: + $ref: "#/components/examples/ReportMultiAssetActivity" + withdrawalsOnly: + $ref: "#/components/examples/ReportWithdrawalsOnly" + emptyPeriod: + $ref: "#/components/examples/ReportEmptyPeriod" + "401": + $ref: "#/components/responses/UnauthorizedError" + "422": + description: Invalid query parameter or filter expression. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + examples: + missingPeriodStart: + $ref: "#/components/examples/ReportMissingPeriodStart" + periodEndNotAfterStart: + $ref: "#/components/examples/ReportPeriodEndNotAfterStart" + # ── Beneficiaries ───────────────────────────────────────────────── /api/beneficiaries: post: diff --git a/docs.json b/docs.json index 83c18d4..0545612 100644 --- a/docs.json +++ b/docs.json @@ -139,6 +139,13 @@ "api-reference/fx-payment/operations/list-operations" ] }, + { + "group": "Reports", + "icon": "chart-line", + "pages": [ + "api-reference/fx-payment/reports/get-report" + ] + }, { "group": "Quotes", "icon": "scale-balanced", From f741cd7346f19552d59a8d508484e0fbe3a5144d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Mon, 4 May 2026 18:35:36 -0300 Subject: [PATCH 3/5] docs(api): include tags in create-operation response examples Backfills the tags array (previously missing) in the pixWithdrawal, brlToUsdSwap, and pixDeposit named examples so the rendered response panel matches the OperationResponse schema, which already lists tags as required. --- apis/fx-payment/openapi.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index 01e5e87..03fdee0 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -2432,6 +2432,11 @@ paths: decimals: 2 fees: [] transactions: [] + tags: + - key: "psp" + value: "Amazon" + - key: "compliance-tier" + value: "high" currentState: status: "REQUESTED" createdAt: "2026-04-25T02:39:17Z" @@ -2656,6 +2661,11 @@ paths: decimals: 2 fees: [] transactions: [] + tags: + - key: "psp" + value: "Amazon" + - key: "compliance-tier" + value: "high" currentState: status: "REQUESTED" createdAt: "2026-04-27T14:12:33Z" @@ -2818,6 +2828,11 @@ paths: decimals: 2 fees: [] transactions: [] + tags: + - key: "psp" + value: "Amazon" + - key: "compliance-tier" + value: "high" currentState: status: "REQUESTED" createdAt: "2026-04-27T15:08:21Z" From e716a157fc0bf1efe56b6ce5dbfd18094353cd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Mon, 4 May 2026 18:46:07 -0300 Subject: [PATCH 4/5] docs(api): add tags to OperationEvent webhook payload Mirrors the AccountResponse/OperationResponse pattern. Introduces TagEvent in fx-webhook to keep the *Event suffix convention. Documented ahead of the Kotlin OperationEvent class, which will need a matching field on the wire. --- apis/fx-webhook/openapi.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index b1fe065..39e6cac 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -1057,6 +1057,27 @@ components: - relationshipType - instruction - atTime + TagEvent: + type: object + description: > + A label attached to a resource. Tags are system-managed — Trace + assigns them based on routing, compliance, and operational criteria. + properties: + key: + type: string + nullable: true + description: > + Category for keyed tags (e.g., `psp`, `compliance-tier`). + `null` for single-value labels. + example: "psp" + value: + type: string + description: > + For keyed tags, the right-hand side of the label. For + single-value labels, the entire tag. + example: "Amazon" + required: + - value OperationEvent: type: object description: > @@ -1094,6 +1115,16 @@ components: description: Underlying rail transactions executed for this operation. items: $ref: "#/components/schemas/TransactionEvent" + tags: + type: array + description: System-managed labels stamped onto the operation when it is created. Inherited from the account's propagating tags. + items: + $ref: "#/components/schemas/TagEvent" + example: + - key: "psp" + value: "Amazon" + - key: "compliance-tier" + value: "high" atTime: type: string format: date-time @@ -1107,6 +1138,7 @@ components: - intent - fees - transactions + - tags - atTime paths: From 487d27d98f979d009d2bac9f8e9fefdca0e29602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Mon, 4 May 2026 18:52:59 -0300 Subject: [PATCH 5/5] docs(api): simplify reports + tags PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tags is optional on OperationResponse and OperationEvent — the live OperationApiResponse and OperationEvent Kotlin classes don't yet carry the field, so leaving required would force strict consumers to fail against today's wire payloads. AccountResponse keeps tags required since the live AccountResponse Kotlin class already exposes it. Trims the Filters parameter override on /api/reports to drop the LHS Brackets preamble already covered by the shared Filters description, leaving only the report-specific examples and a pointer to the Filtering guide. --- apis/fx-payment/openapi.yml | 5 +---- apis/fx-webhook/openapi.yml | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index 03fdee0..683da86 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -1155,7 +1155,6 @@ components: - quote - fees - transactions - - tags - currentState - states - createdAt @@ -3245,8 +3244,6 @@ paths: - $ref: "#/components/parameters/PeriodEnd" - $ref: "#/components/parameters/Filters" description: | - Filter expression using LHS Brackets syntax (`field[operator]=value`). Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). - Common filters for reports: - `account.id[eq]=a1b2c3d4-5e6f-7890-abcd-ef1234567890` — single account @@ -3254,7 +3251,7 @@ paths: - `tags.key[eq]=psp` — operations carrying a tag with this key - `tags.value[eq]=Amazon` — operations carrying a tag with this value - See the [Filtering](/guides/principles/filtering) guide for the full operator catalog. + See the [Filtering](/guides/principles/filtering) guide for the LHS Brackets syntax and the full operator catalog. responses: "200": description: Aggregated report for the requested window. diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index 39e6cac..9c8e134 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -1138,7 +1138,6 @@ components: - intent - fees - transactions - - tags - atTime paths: