diff --git a/api-reference/fx-webhook/events/account/asset-activated.mdx b/api-reference/fx-webhook/events/account/asset-activated.mdx index 6687546..ee54369 100644 --- a/api-reference/fx-webhook/events/account/asset-activated.mdx +++ b/api-reference/fx-webhook/events/account/asset-activated.mdx @@ -1,5 +1,5 @@ --- -title: "ACCOUNT_ASSET_ACTIVATED" +title: "ASSET_ACTIVATED" description: "Fires when an account asset finishes onboarding and funding instructions are available." -openapi: "apis/fx-webhook/openapi.yml webhook ACCOUNT_ASSET_ACTIVATED" +openapi: "apis/fx-webhook/openapi.yml webhook ASSET_ACTIVATED" --- diff --git a/api-reference/fx-webhook/events/operation/operation-completed.mdx b/api-reference/fx-webhook/events/operation/operation-completed.mdx new file mode 100644 index 0000000..3c3998e --- /dev/null +++ b/api-reference/fx-webhook/events/operation/operation-completed.mdx @@ -0,0 +1,5 @@ +--- +title: "OPERATION_COMPLETED" +description: "Fires when a payment operation reaches its terminal success state." +openapi: "apis/fx-webhook/openapi.yml webhook OPERATION_COMPLETED" +--- diff --git a/api-reference/fx-webhook/events/operation/operation-failed.mdx b/api-reference/fx-webhook/events/operation/operation-failed.mdx new file mode 100644 index 0000000..a4cd99f --- /dev/null +++ b/api-reference/fx-webhook/events/operation/operation-failed.mdx @@ -0,0 +1,5 @@ +--- +title: "OPERATION_FAILED" +description: "Fires when a payment operation reaches its terminal failure state." +openapi: "apis/fx-webhook/openapi.yml webhook OPERATION_FAILED" +--- diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index 97fad92..04a7df9 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -140,11 +140,13 @@ components: type: string description: The specific event type within a resource, sent as the `X-Event-Type` header on each delivery. enum: - - ACCOUNT_ASSET_ACTIVATED + - ASSET_ACTIVATED - BENEFICIARY_PAYMENT_INSTRUCTION_CREATED - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED - BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED - OPERATION_REQUESTED + - OPERATION_COMPLETED + - OPERATION_FAILED example: OPERATION_REQUESTED ExecutionLogStatus: type: string @@ -1079,11 +1081,13 @@ components: OperationEvent: type: object description: > - Payload delivered when an operation is first created - (`OPERATION_REQUESTED`). Subsequent state transitions - (`PROCESSING`, `COMPLETED`, `FAILED`, etc.) are not published as - webhook events — poll `GET /api/operations/{operationId}` to - track lifecycle progress. + Payload delivered for operation lifecycle events + (`OPERATION_REQUESTED`, `OPERATION_COMPLETED`, `OPERATION_FAILED`). + Inspect the `X-Event-Type` header — or `currentState.status` on + the payload — to tell which transition fired. Intermediate + statuses (`PROCESSING`, `ON_HOLD`, `ACTION_REQUIRED`) are not + published as webhooks; fetch + `GET /api/operations/{operationId}` if you need them. properties: id: type: string @@ -1091,6 +1095,8 @@ components: readOnly: true customerId: type: string + format: uuid + example: "9c7b6a2e-1d3f-4a5b-8c9d-0e1f2a3b4c5d" account: $ref: "#/components/schemas/AccountReferenceEvent" sourceAmount: @@ -1123,6 +1129,9 @@ components: value: "Amazon" - key: "compliance-tier" value: "high" + currentState: + $ref: "#/components/schemas/OperationState" + description: Status the operation is in at the time the event fires. atTime: type: string format: date-time @@ -1136,7 +1145,57 @@ components: - intent - fees - transactions + - currentState - atTime + OperationStatus: + type: string + description: Lifecycle status of an operation. + enum: + - REQUESTED + - PROCESSING + - ON_HOLD + - ACTION_REQUIRED + - COMPLETED + - FAILED + example: COMPLETED + Reason: + type: object + description: Machine- and human-readable explanation. Populated on an `OperationState` when the status transition requires justification (e.g., `FAILED`). + properties: + code: + type: string + description: Machine-readable code. + example: "INSUFFICIENT_BALANCE" + message: + type: string + description: Human-readable description. + example: "Insufficient balance for the requested operation" + details: + type: object + additionalProperties: true + description: Free-form context relevant to the reason. + required: + - code + - message + - details + OperationState: + type: object + description: A single entry in an operation's state history. Carries a status, optional reason, and the time the state was entered. + properties: + status: + $ref: "#/components/schemas/OperationStatus" + reason: + allOf: + - $ref: "#/components/schemas/Reason" + nullable: true + description: Justification for entering this status. Present for `FAILED`; `null` for `REQUESTED` and `COMPLETED`. + createdAt: + type: string + format: date-time + description: Time the operation entered this status. + required: + - status + - createdAt paths: /api/subscriptions: @@ -1466,7 +1525,7 @@ paths: $ref: "#/components/schemas/ResourceReference" webhooks: - ACCOUNT_ASSET_ACTIVATED: + ASSET_ACTIVATED: post: summary: Account asset activated description: Fires when an account asset finishes onboarding and funding instructions are available. @@ -1544,7 +1603,49 @@ webhooks: OPERATION_REQUESTED: post: summary: Operation requested - description: Fires when a payment operation (deposit, withdrawal, swap) is created. + description: > + Fires when a payment operation (deposit, withdrawal, transfer, + swap) is created. Compliance review and rail processing happen + after this event — wait for `OPERATION_COMPLETED` or + `OPERATION_FAILED` to know the terminal outcome. + tags: + - Operation + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/OperationEvent" + responses: + "200": + $ref: "#/components/responses/WebhookAck" + OPERATION_COMPLETED: + post: + summary: Operation completed + description: > + Fires when a payment operation reaches its terminal success + state. The payload carries `currentState.status: COMPLETED`, + with `transactions` populated by the rail-confirmed legs that + settled the operation. + tags: + - Operation + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/OperationEvent" + responses: + "200": + $ref: "#/components/responses/WebhookAck" + OPERATION_FAILED: + post: + summary: Operation failed + description: > + Fires when a payment operation reaches its terminal failure + state. The payload carries `currentState.status: FAILED` and + `currentState.reason` describing why. No further events fire + for this operation. tags: - Operation requestBody: diff --git a/docs.json b/docs.json index 3298212..6159423 100644 --- a/docs.json +++ b/docs.json @@ -219,7 +219,9 @@ "group": "Operation", "icon": "arrows-rotate", "pages": [ - "api-reference/fx-webhook/events/operation/operation-requested" + "api-reference/fx-webhook/events/operation/operation-requested", + "api-reference/fx-webhook/events/operation/operation-completed", + "api-reference/fx-webhook/events/operation/operation-failed" ] }, { diff --git a/journeys/deposit.mdx b/journeys/deposit.mdx index 50f8fd5..02a3acc 100644 --- a/journeys/deposit.mdx +++ b/journeys/deposit.mdx @@ -61,7 +61,7 @@ Deposits credit an account when funds arrive via the chosen funding rail. The cu - Poll `GET /api/operations/{operationId}` until the inbound payment is reconciled and the operation transitions to `COMPLETED`. Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. + Subscribe to `OPERATION_COMPLETED` to receive the deposit confirmation once the inbound payment is reconciled, or `OPERATION_FAILED` if reconciliation fails. Both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). The intermediate `PROCESSING` status is not published as a webhook; poll `GET /api/operations/{operationId}` if you need to surface it in your UI. ```bash curl --request GET \ diff --git a/journeys/swap.mdx b/journeys/swap.mdx index 7a5716c..210f684 100644 --- a/journeys/swap.mdx +++ b/journeys/swap.mdx @@ -54,7 +54,7 @@ Swaps convert funds between assets within the same account — for example, BRL - Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. + Subscribe to `OPERATION_COMPLETED` and `OPERATION_FAILED` to receive the terminal outcome — both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). The intermediate `PROCESSING` status is not published as a webhook; poll `GET /api/operations/{operationId}` if you need to surface it in your UI. ```bash curl --request GET \ diff --git a/journeys/transfer.mdx b/journeys/transfer.mdx index c7bca3f..0739a1a 100644 --- a/journeys/transfer.mdx +++ b/journeys/transfer.mdx @@ -38,7 +38,7 @@ Transfers move funds between two accounts owned by the same customer. They are s - Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. + Subscribe to `OPERATION_COMPLETED` and `OPERATION_FAILED` to receive the terminal outcome — both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). The intermediate `PROCESSING` status is not published as a webhook; poll `GET /api/operations/{operationId}` if you need to surface it in your UI. ```bash curl --request GET \ diff --git a/journeys/withdrawal.mdx b/journeys/withdrawal.mdx index 67cea85..5645eb5 100644 --- a/journeys/withdrawal.mdx +++ b/journeys/withdrawal.mdx @@ -95,7 +95,7 @@ Withdrawals move funds out of an account to an external destination — a bank a - Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Compliance review can pause the operation in `ON_HOLD` or `ACTION_REQUIRED` until additional checks clear. Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. + Subscribe to `OPERATION_COMPLETED` and `OPERATION_FAILED` to receive the terminal outcome — both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). Intermediate statuses (`PROCESSING`, `ON_HOLD`, `ACTION_REQUIRED`) are not published as webhooks; poll `GET /api/operations/{operationId}` if you need to surface them in your UI. ```bash curl --request GET \ diff --git a/webhooks/overview.mdx b/webhooks/overview.mdx index 4d7f626..c53eaa2 100644 --- a/webhooks/overview.mdx +++ b/webhooks/overview.mdx @@ -66,15 +66,19 @@ Browse the full event catalog by resource: | Resource | Events | Description | | --- | --- | --- | | [Account](/api-reference/fx-webhook/events/account/asset-activated) | 1 | Asset onboarding completion | -| [Operation](/api-reference/fx-webhook/events/operation/operation-requested) | 1 | Operation creation | +| [Operation](/api-reference/fx-webhook/events/operation/operation-requested) | 3 | Operation creation, completion, and failure | | [Beneficiary](/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created) | 3 | Payment instruction creation, approval, and rejection | You can also fetch the catalog programmatically: [`GET /references/ResourceName/all`](/api-reference/fx-webhook/subscriptions/list-resource-references). - Webhook events fire on **creation and review outcomes only**. Operation - lifecycle transitions (`PROCESSING`, `COMPLETED`, `FAILED`, `ON_HOLD`, - `ACTION_REQUIRED`) and account state changes are **not** published as - webhooks — poll `GET /api/operations/{operationId}` or - `GET /api/accounts/{accountId}` to track them. + Operations publish webhooks on creation (`OPERATION_REQUESTED`) and + on terminal outcomes (`OPERATION_COMPLETED`, `OPERATION_FAILED`). + Intermediate statuses (`PROCESSING`, `ON_HOLD`, `ACTION_REQUIRED`) + are not published — poll `GET /api/operations/{operationId}` if + you need them. + + Accounts publish only `ASSET_ACTIVATED`. Other account state + changes are not published — poll `GET /api/accounts/{accountId}` if + you need them.