# Create management session Source: https://docs.paysway.io/api-reference/beneficiary-hub/create-management-session /api-reference/paysway-api.yaml post /beneficiaries/organizations/sessions Creates a Beneficiary Hub management session for an organization end-user to manage their beneficiary details. # Get recipient Source: https://docs.paysway.io/api-reference/beneficiary-hub/get-recipient /api-reference/paysway-api.yaml get /beneficiaries/organizations/recipients/{recipientId} Retrieves a recipient of an organization end-user by the recipient ID # List recipients Source: https://docs.paysway.io/api-reference/beneficiary-hub/list-recipients /api-reference/paysway-api.yaml get /beneficiaries/organizations/recipients Retrieves a list of recipients for an organization end-user by their external user ID. # Recipient events Source: https://docs.paysway.io/api-reference/beneficiary-hub/recipient-events /api-reference/paysway-api.yaml webhook recipientEvents # Bulk enrich payment details Source: https://docs.paysway.io/api-reference/payment-preparation/bulk-enrich-payment-details /api-reference/paysway-api.yaml post /payments/bulk-enrichments Enriches multiple payment instructions in a single request. Each item follows the same schema as POST /payments/enrichments. Results are returned in the same order as the input. The maximum number of items per request is 20. # Bulk validate payment details Source: https://docs.paysway.io/api-reference/payment-preparation/bulk-validate-payment-details /api-reference/paysway-api.yaml post /payments/bulk-validations Validates multiple payment instructions in a single request. Each item follows the same schema as POST /payments/validations and produces the same alert codes. Results are returned in the same order as the input. The maximum number of items per request is 20. # Enrich payment details Source: https://docs.paysway.io/api-reference/payment-preparation/enrich-payment-details /api-reference/paysway-api.yaml post /payments/enrichments Transforms and completes the fields of the payment instruction to ensure payment success and enhance user experience. It converts account numbers and bank details into formats acceptable for the selected payment method, provides routing inputs such as intermediary banks and supported payment schemes, and returns details such as bank name, address, and logo. # Validate payment details Source: https://docs.paysway.io/api-reference/payment-preparation/validate-payment-details /api-reference/paysway-api.yaml post /payments/validations Accepts the full or partial payment instruction and validates it against local and global payment regulations, scheme requirements, bank reference data, and heuristics. If the payment details in the instruction are likely to be valid, the response will indicate success. If there are issues, the response will include a list of validation errors with specific reasons. # Create webhook subscription Source: https://docs.paysway.io/api-reference/webhooks/create-webhook-subscription /api-reference/paysway-api.yaml post /webhooks/subscriptions Creates a webhook subscription for an organization to receive notifications about events. # Delete webhook subscription Source: https://docs.paysway.io/api-reference/webhooks/delete-webhook-subscription /api-reference/paysway-api.yaml delete /webhooks/subscriptions/{subscriptionId} Deletes a webhook subscription by the subscription ID # Get webhook subscription Source: https://docs.paysway.io/api-reference/webhooks/get-webhook-subscription /api-reference/paysway-api.yaml get /webhooks/subscriptions/{subscriptionId} Retrieves a webhook subscription by the subscription ID # List webhook subscriptions Source: https://docs.paysway.io/api-reference/webhooks/list-webhook-subscriptions /api-reference/paysway-api.yaml get /webhooks/subscriptions Retrieves a list of webhook subscriptions for an organization # Update webhook subscription Source: https://docs.paysway.io/api-reference/webhooks/update-webhook-subscription /api-reference/paysway-api.yaml patch /webhooks/subscriptions/{subscriptionId} Updates a webhook subscription by the subscription ID # Management sessions Source: https://docs.paysway.io/beneficiary-hub/management-sessions A management session is a short‑lived object that allows a beneficiary to manage payout methods in a PaySway-hosted UI. ## Create a session You can create a new management session by sending the following request: [**POST** /beneficiaries/organizations/sessions](/api-reference/beneficiary-hub/create-management-session) ```json Request theme={null} { "externalUserId": "1dda592e-5b1d-47d2-b1de-e772fca15327", "returnUrl": "https://tenant.tld/return-url" } ``` ```json Response theme={null} { "id": "4479b65d-a33b-43e4-9d3b-3b3f159cbf52", "url": "https://payouts.tenant.tld/sessions/4479b65d-a33b-43e4-9d3b-3b3f159cbf52#ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJ..." } ``` ## Session options You can customize the beneficiary management session by providing the `options` field in your request. This allows you to restrict what a beneficiary can input. The available options are: | Field | Type | Description | | :---------------------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------- | | `allowedRecipientTypes` | `string[]` | Limit the session to `INDIVIDUAL` or `BUSINESS` recipients. If omitted the user may pick either type. | | `allowedCountries` | `string[]` | List of ISO 3166 alpha‑2 country codes (e.g., `GB`, `US`). If omitted the user may add payout methods for any supported country. | **Example with options** [**POST** /beneficiaries/organizations/sessions](/api-reference/beneficiary-hub/create-management-session) ```json Request theme={null} { "externalUserId": "1dda592e-5b1d-47d2-b1de-e772fca15327", "returnUrl": "https://tenant.tld/return-url", "options": { "allowedRecipientTypes": ["INDIVIDUAL"], "allowedCountries": ["GB", "DE"] } } ``` # Overview Source: https://docs.paysway.io/beneficiary-hub/overview The **Beneficiary Hub** lets your beneficiaries (the end‑users of your application) review and edit their payout methods through PaySway's smart, country‑aware forms. We believe in asking only what's necessary, setting clear expectations, and reducing maintenance and development efforts for your team - so you can focus on your core product and not on ever‑changing payment rails. Our operational model is: The process begins when an end-user opts to manage their payout methods within your application's interface. Your application backend then requests a short-lived, secure management session from the PaySway API. This session is unique to the end-user. We return a URL that you use to redirect the end-user to. Once redirected, the end-user interacts directly with the PaySway Beneficiary Hub. Here, they can securely add, view, edit, or delete their payout methods. Our interface uses smart forms with dynamic validation and data enrichment to ensure accuracy and ease of use. Your system stays updated with any changes through webhooks. The PaySway API can send notifications to your application, allowing you to retrieve the latest payout methods for your users as needed. ```mermaid theme={null} sequenceDiagram participant user as End-user participant client_ui as Client UI participant client_app as Client app participant paysway_ui as PaySway-hosted UI participant paysway_api as PaySway API user->>client_ui: Click 'Manage payout methods' button client_ui->>client_app: Request to manage payout methods client_app->>paysway_api: Generate short-lived management session (provides external user ID) paysway_api-->>client_app: Return short-lived session URL client_app-->>client_ui: Return session URL to redirect to client_ui-->>user: Redirect to session URL user->>paysway_ui: Accesses management session paysway_ui->>paysway_api: Authenticate user with session secret paysway_api-->>paysway_ui: Authentication successful paysway_ui-->>user: Display payout methods portal user->>paysway_ui: View/Add/Edit/Delete payout methods (smart forms, dynamic validation, enrichment) paysway_ui->>paysway_api: Send payout method changes (on save) paysway_api-->>paysway_ui: Confirm changes saved loop Asynchronous updates paysway_api-)client_app: Notify of payout method updates via webhooks client_app->>paysway_api: Retrieve updated payout method details for a user paysway_api-->>client_app: Payout method details end ``` # Payout methods Source: https://docs.paysway.io/beneficiary-hub/payout-methods Keeping your beneficiaries' payout method details in sync means keeping their **recipient** records - the API objects that store each individual payout method - up to date. **Beneficiary** (end‑user): the person or business that owns the payout methods. **Payout method**: a bank account, E-wallet, card or similar destination. **Recipient**: the API resource that stores one payout method. A beneficiary can therefore have several recipients. ## Real-time updates To maintain the most current recipient information, we strongly advise setting up webhooks and subscribing to `RECIPIENT_*` event types. This ensures your system is immediately notified of any changes or updates to recipient details, such as when a beneficiary adds, updates, or removes their information through a management session. For detailed instructions on how to set up and manage webhooks, please refer to our [webhooks guide](/webhooks/overview). ## Retrieving recipient data via API While webhooks notify you of changes, the way to get the complete, updated recipient details is to call the API after you receive a relevant webhook event. ### 1. Get Recipient by ID After receiving a webhook for a specific recipient, you should use the `id` from the webhook event to retrieve their full, updated details. This is the most direct way to get the latest information because all `RECIPIENT_*` webhook events include the `id` of the affected recipient. [**GET** /beneficiaries/organizations/recipients/\{recipientId}](/api-reference/beneficiary-hub/get-recipient) ```json Response theme={null} { "id": "06726a35-9f90-49b9-a5ef-0a1d97d28ff5", "externalUserId": "d6189905-7fd5-4e79-9496-8f62184649c6", "country": "US", "currency": "USD", "method": "BANK_ACCOUNT", "isDefault": false, "creditorAgent": { "clearingSystemMemberId": { "memberId": "021000021" } }, "creditorAccount": { "id": { "value": "12345678", "type": "ACCOUNT_NUMBER" }, "type": "CHECKING" }, "creditor": { "type": "INDIVIDUAL", "name": "John Doe", "address": { "addressLine1": "620 8th Ave", "townName": "New York", "postCode": "10018", "countrySubdivision": "NY", "country": "US" }, "documents": [] } } ``` ### 2. List recipients by external user ID You can retrieve all recipients associated with a specific one of your end-users using the ID you provided when creating the management session. This endpoint is helpful if you want to synchronize the state of all recipients for a given end-user or to simply list all recipients they have added. [**GET** /beneficiaries/organizations/recipients](/api-reference/beneficiary-hub/list-recipients) **Query parameters**: The identifier of the end-user whose recipients you want to retrieve. ```json Response theme={null} { "items": [ { "id": "6f10711d-15b1-466f-ae5b-68ee81ac5f51", "externalUserId": "d6189905-7fd5-4e79-9496-8f62184649c6", ... }, { "id": "06726a35-9f90-49b9-a5ef-0a1d97d28ff5", "externalUserId": "d6189905-7fd5-4e79-9496-8f62184649c6", ... }, ] } ``` # Authentication Source: https://docs.paysway.io/get-started/authentication Our API uses **M2M clients** to authenticate your application. Each M2M client has a `client_id` and `client_secret`, and uses the OAuth2 client credentials flow to obtain a short-lived access token. ## Obtain an access token **POST /oauth2/token** Must be `application/x-www-form-urlencoded`. Must be `client_credentials`. Your M2M client ID. Your M2M client secret. ```shell Request theme={null} curl --request POST \ --url 'https://api.paysway.dev/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data grant_type=client_credentials \ --data client_id=YOUR_CLIENT_ID \ --data client_secret=YOUR_CLIENT_SECRET ``` ```json Response theme={null} { "access_token": "ACCESS_TOKEN", "token_type": "Bearer", "expires_in": 3600 } ``` ## Use the token Include the token in the `Authorization` header of every API request: `Bearer YOUR_ACCESS_TOKEN` ```shell theme={null} curl --request POST \ --url https://api.paysway.dev/payments/validations \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' ``` Tokens expire after 3600 seconds. If you receive a `401 Unauthorized`, request a new token. Consider refreshing proactively before expiry to avoid interruptions. # Environments Source: https://docs.paysway.io/get-started/environments We provide distinct environments to support your development lifecycle, from initial integration and testing to live production traffic. Each environment has a unique base URL for API requests. | Environment | Base URL | Use For | | :-------------- | :------------------------ | :----------------------------------------------------------------------------------------------------- | | **Production** | `https://api.paysway.io` | Live and fully tested integrations | | **Development** | `https://api.paysway.dev` | All development and testing. Experiment, debug, and run end-to-end tests here. Data is fully isolated. | # Errors Source: https://docs.paysway.io/get-started/errors PaySway uses standard HTTP response codes to indicate the success or failure of an API request. * Codes in the `2xx` range indicate success. * Codes in the `4xx` range indicate a client error (e.g., a required parameter was omitted, validation failed, etc.). * Codes in the `5xx` range indicate an error with PaySway's servers (these are rare). Some `4xx` errors include an error code to identify the specific issue (e.g., access token is invalid). ## Error response PaySway returns an error response for `4xx` and `5xx` status codes with the following fields: A unique error code identifying the specific issue. Only included for well-known errors. See the error codes section below for possible values. A human-readable message providing details about the error. Intended for developers and may contain technical information. ## Error codes Below is a reference of PaySway error codes, including descriptions and resolution guidance where applicable. ### Idempotency errors | Error code | Error description | | :------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | *IDEMPOTENCY\_KEY\_HEADER\_MISSING* | The required `Idempotency-Key` header is missing. | | *IDEMPOTENCY\_KEY\_REQUEST\_MISMATCH* | Idempotency keys can only be used with the same parameters they were first used with. Try using a different key if you meant to execute a different request. | ### Authentication errors | Error code | Error description | | :----------------------------------- | :--------------------------------------------------------------------------------------------- | | *AUTH\_MISSING\_CLIENT\_CREDENTIALS* | Client credentials are missing. | | *AUTH\_INVALID\_CLIENT\_CREDENTIALS* | Provided client credentials are invalid. | | *AUTH\_M2M\_TOKEN\_NOT\_FOUND* | No M2M access token found. Please provide your M2M access token in the `Authorization` header. | | *AUTH\_M2M\_TOKEN\_INVALID* | Provided M2M token is expired, malformed, or invalid for other reasons. | ### Webhook errors | Error code | Error description | | :--------------------------------------- | :------------------------------------------------------------ | | *WEBHOOK\_SUBSCRIPTIONS\_LIMIT\_REACHED* | The maximum number of webhook subscriptions has been reached. | | *WEBHOOK\_ENDPOINT\_URL\_INVALID* | The endpoint URL does not meet the specified requirements. | | *WEBHOOK\_ENDPOINT\_ALREADY\_SUBSCRIBED* | A webhook subscription with this endpoint URL already exists. | # Idempotency Source: https://docs.paysway.io/get-started/idempotency Idempotency ensures that repeating a request always produces the intended outcome, no matter how many times it's sent. This is especially important when network errors or interruptions make it unclear whether an operation has already completed. For operations where duplicates could cause unintended effects, PaySway requires an idempotency key. Including the same key in a retried request lets PaySway recognize it as a duplicate and return the existing result instead of performing the operation again. This ensures you can safely retry requests without the risk of creating duplicates or executing the same action twice. ## Implementation The idempotency key must be a valid UUID. Generate a unique UUID for each distinct request you intend to make. ```javascript JavaScript theme={null} import { v4 as uuidv4 } from 'uuid'; const idempotencyKey = uuidv4(); ``` ```python Python theme={null} import uuid idempotency_key = str(uuid.uuid4()) ``` ```java Java theme={null} import java.util.UUID; UUID idempotencyKey = UUID.randomUUID(); ``` ```csharp C# theme={null} using System; Guid idempotencyKey = Guid.NewGuid() ``` ```go Go theme={null} import "github.com/google/uuid" idempotencyKey := uuid.New() ``` Add the `Idempotency-Key` header to your HTTP request with the generated UUID. ```http theme={null} Idempotency-Key: 9678e160-38b3-4182-851e-9ca1d925f6ce ``` When you need to retry a previous request due to network issues or timeouts, always use the same idempotency key as the original request. Store the idempotency key alongside your request metadata until you receive a successful response. This way, retries for the same entity state will always result in the same key. If a retried request uses the same idempotency key but different parameters, PaySway will reject it. This prevents accidental misuse and ensures the idempotency key always refers to the same request. # Introduction Source: https://docs.paysway.io/get-started/introduction PaySway offers a suite of API-based solutions designed to simplify complex payment-related operations. Ensure successful transfers, enhance user experience, and minimize maintenance with our range of APIs and services. Learn how to authenticate your API requests. Validate and enrich payment instructions. Collect and manage beneficiary payment details. Receive real-time notifications for events. Explore detailed API endpoint documentation. ## Need help? If you have any questions as you explore our documentation or implement our solutions, please don't hesitate to reach out. You can always: * Schedule a call with us: **[book a meeting](https://calendly.com/paysway/intro)** * Email our team: **[dmitry@paysway.io](mailto:dmitry@paysway.io)** or **[oleg@paysway.io](mailto:oleg@paysway.io)** # Versioning Source: https://docs.paysway.io/get-started/versioning Our primary goal is to provide a stable API that you can rely on for your integrations. We currently operate on an unversioned API, and we are committed to making backward-compatible changes. Should we need to introduce backward-incompatible (breaking) changes, we will release a new, distinct API version (e.g., `v2`). The previous version will be maintained and supported for a reasonable deprecation period to allow clients to migrate. We will provide clear timelines and guidance for any such transitions. ### Backward-compatible changes We regularly release backward-compatible enhancements and features. Your integration should continue to work seamlessly when these changes are deployed, though you may want to update your code to take advantage of new capabilities. Non-breaking changes include: * Adding new API endpoints * Adding new optional parameters or fields to existing API request bodies or query strings * Adding new fields to API responses or webhook payloads * Adding new webhook event types * Adding new values to existing enums This is particularly important for fields like [validation alert codes](/payment-preparation/validate-payment#table-with-the-alert-codes) or [error codes](/get-started/errors#error-codes). As our product evolves, new codes will be added. Client applications should be designed to gracefully handle unrecognized enum values in a non-blocking manner, rather than failing the process. * Changing the descriptive `message` field in [error responses](/get-started/errors) # Enrich payment Source: https://docs.paysway.io/payment-preparation/enrich-payment ## Enrich by BIC or bank identifier You might want to expose additional information about the bank such as bank name, address, and logo. [**POST** /payments/enrichments](/api-reference/payment-preparation/enrich-payment-details) ```json Request theme={null} { "creditorAgent": { "bic": "BARCUS33" } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "BARCLAYS BANK PLC", "address": { "addressLine1": "745 SEVENTH AVENUE", "addressLine3": "NEW YORK NY 10019, US", "streetName": "SEVENTH AVENUE", "buildingNumber": "745", "townName": "NEW YORK", "postCode": "10019", "countrySubdivision": "NEW YORK", "country": "US" }, "bic": "BARCUS33XXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/barclays.png" } } } ``` ```json Request theme={null} { "country": "GB", "creditorAgent": { "clearingSystemMemberId": { "memberId": "609242" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "JPMORGAN CHASE BANK, N.A.", "address": { "addressLine1": "HAMPSHIRE BUILDING, 1ST FLOOR", "addressLine2": "CHASESIDE", "addressLine3": "BOURNEMOUTH, BH7 7DA, DORSET GB", "department": "EMEA TREASURY SERVICES OPER", "streetName": "CHASESIDE", "buildingName": "HAMPSHIRE BUILDING", "floor": "1ST FLOOR", "townName": "BOURNEMOUTH", "postCode": "BH7 7DA", "countrySubdivision": "DORSET", "country": "GB" }, "bic": "CHASGB2LXXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/GB_609242.png" }, "settlementInformation": { "settlementSystems": [ "GB_BACS_CR", "GB_CCCC", "GB_BACS_DR", "GB_CHAPS", "GB_FPS" ] } } } ``` ## Enrich by IBAN or account number You don't have to populate the BIC when you have the IBAN and rely on enrichment instead. It's particularly relevant for SEPA transfers and local currency transfers in countries like CZ, DK, NO. There are two types of BICs typically associated with the IBAN: SEPA BIC (can be generalized as IBAN BIC for cases when the country is not in SEPA), for example, REVOGB21, and ROUTING BIC, like REVOGB2L. We default to the IBAN BIC in the Enrichment endpoint, unless there's a linked BIC (ROUTING BIC) for the relevant record in the banking directory that has a better chance of payment success thanks to the explicit mention of the support of the selected currency through correspondents. [**POST** /payments/enrichments](/api-reference/payment-preparation/enrich-payment-details) ```json Request theme={null} { "creditorAccount": { "id": { "value": "PT50002300004569522628094", "type": "IBAN" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "BANCO ACTIVOBANK, SA", "address": { "addressLine1": "RUA AUGUSTA RC 84", "addressLine3": "LISBON, LISBON, 1100-053", "streetName": "RUA AUGUSTA RC", "buildingNumber": "84", "townName": "LISBON", "postCode": "1100-053", "countrySubdivision": "LISBON", "country": "PT" }, "bic": "ACTVPTPLXXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/ACTVPTPLXXX.png" }, "settlementInformation": { "settlementSystems": [ "SCT", "SDD_CORE", "SCT_INST" ] } } } ``` ```json Request theme={null} { "country": "HU", "creditorAccount": { "id": { "value": "126000161707899601473248", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "WISE EUROPE SA", "address": { "addressLine1": "TRONE RUE 100", "addressLine3": "BRUSSELS, BRUXELLES-CAPITALE, 1050", "streetName": "TRONE RUE", "buildingNumber": "100", "townName": "BRUSSELS", "postCode": "1050", "countrySubdivision": "BRUXELLES-CAPITALE", "country": "BE" }, "bic": "TRWIBEBBXXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/TRWIBEBBXXX.png" }, "creditorAccount": { "ids": [ { "value": "126000161707899601473248", "type": "ACCOUNT_NUMBER" }, { "value": "HU78126000161707899601473248", "type": "IBAN" } ] } } } ``` ```json Request theme={null} { "country": "SE", "creditorAccount": { "id": { "value": "8937028383339", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "SWEDBANK AB (PUBL)", "address": { "addressLine1": "LANDSVAGEN 40", "addressLine2": "SUNDBYBERG", "addressLine3": "STOCKHOLM, STOCKHOLM, 105 34", "streetName": "LANDSVAGEN", "buildingNumber": "40", "townName": "STOCKHOLM", "townLocationName": "SUNDBYBERG", "postCode": "105 34", "countrySubdivision": "STOCKHOLM", "country": "SE" }, "bic": "SWEDSESSXXX", "clearingSystemMemberId": { "memberId": "89370" }, "logo": "https://cdn.paysway.dev/assets/bank-logos/swedbank.png" }, "creditorAccount": { "ids": [ { "value": "28383339", "type": "ACCOUNT_NUMBER" }, { "value": "SE9680000893700028383339", "type": "IBAN" } ] } } } ``` ## Enrich chain of intermediaries You can completely avoid asking your users for intermediary BICs and rely on our API instead. Just populate the currency to get the intermediary banks accurately. You should typically indicate the last intermediary BIC (out of 3) in your payment instructions to partners. [**POST** /payments/enrichments](/api-reference/payment-preparation/enrich-payment-details) ```json Request theme={null} { "creditorAgent": { "bic": "CHASGB2L" }, "currency": "NZD" } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "JPMORGAN CHASE BANK, N.A.", "address": { "addressLine1": "25 BANK STREET, CANARY WHARF", "addressLine3": "LONDON, E14 5JP, GREATER LONDON GB", "streetName": "BANK STREET", "buildingNumber": "25", "townName": "LONDON", "townLocationName": "CANARY WHARF", "postCode": "E14 5JP", "countrySubdivision": "GREATER LONDON", "country": "GB" }, "bic": "CHASGB2LXXX" }, "intermediaryAgent1": { "bic": "ANZBNZ22XXX" } } } ``` ```json Request theme={null} { "creditorAgent": { "bic": "MELNUS3P" }, "currency": "GBP" } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "THE BANK OF NEW YORK MELLON", "address": { "addressLine1": "500 GRANT STREET", "addressLine3": "PITTSBURGH PA 15258, US", "streetName": "GRANT STREET", "buildingNumber": "500", "townName": "PITTSBURGH", "postCode": "15258", "countrySubdivision": "PENNSYLVANIA", "country": "US" }, "bic": "MELNUS3PXXX" }, "intermediaryAgent1": { "bic": "IRVTGB2XXXX" }, "intermediaryAgent2": { "bic": "IRVTUS3NXXX" } } } ``` ```json Request theme={null} { "creditorAgent": { "bic": "ABKLCY2NLAR" }, "currency": "AUD" } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "ALPHA BANK CYPRUS LTD", "address": { "addressLine1": "28 ARCH MACARIOS III AVENUE", "addressLine3": "LARNACA, LARNACA, 2324", "streetName": "ARCH MACARIOS III AVENUE", "buildingNumber": "28", "townName": "LARNACA", "postBox": "POB 343", "postCode": "2324", "countrySubdivision": "LARNACA", "country": "CY" }, "bic": "ABKLCY2NLAR", "logo": "https://cdn.paysway.dev/assets/bank-logos/ABKLCY2NXXX.png" }, "intermediaryAgent1": { "bic": "ANZBAU3MXXX" }, "intermediaryAgent2": { "bic": "CHASDEFXXXX" }, "intermediaryAgent3": { "bic": "CRBAGRAAXXX" } } } ``` ## Enrich settlement schemes You can enrich bank identifiers with information about their supported settlement systems. This helps you understand which schemes are available for a specific bank, enabling better routing decisions and user experience. [**POST** /payments/enrichments](/api-reference/payment-preparation/enrich-payment-details) The `settlementSystems` array contains standardized codes representing different payment clearing and settlement systems: | Code | Country | Name | Description | | ---------------- | -------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | **SCT** | SEPA countries | SEPA Credit Transfer | Standard euro credit transfers within SEPA zone | | **SCT\_INST** | SEPA countries | SEPA Instant Credit Transfer | Near-instant euro credit transfers | | **SDD\_CORE** | SEPA countries | SEPA Direct Debit Core\* | Consumer direct debit scheme for recurring payments | | **SDD\_B2B** | SEPA countries | SEPA Direct Debit B2B\* | Business-to-business direct debit scheme | | **US\_FEDWIRE** | United States | Fedwire Funds Service | Real-time gross settlement system for high-value USD transfers | | **US\_FEDACH** | United States | Automated Clearing House System (ACH)\* | Batch processing system for low-value USD transfers | | **GB\_CHAPS** | United Kingdom | Clearing House Automated Payment System (CHAPS) | High-value same-day GBP settlement system | | **GB\_CCCC** | United Kingdom | Cheque and Credit Clearing Company | Paper-based clearing for cheques and credits | | **GB\_FPS** | United Kingdom | Faster Payment System | Near-instant GBP transfers | | **GB\_BACS\_CR** | United Kingdom | Bacs Direct Credit | Batch processing for GBP credit transfers | | **GB\_BACS\_DR** | United Kingdom | Bacs Direct Debit\* | Batch processing for GBP direct debits | | **CA\_ACSS** | Canada | Automated Clearing Settlement System\* | Settlement of cheques and electronic items, such as pre-authorized debits, direct deposits and Interac debit payments | | **CA\_LYNX** | Canada | High Value Transfer System (Lynx) | Real-time settlement for high-value CAD transfers | | **AU\_HVCS** | Australia | High Value Clearing System | Real-time settlement for high-value AUD transfers | | **AU\_RITS** | Australia | Reserve Bank Information and Transfer System | Central bank settlement system for interbank transfers | | **AU\_BECS** | Australia | Bulk Electronic Clearing System\* | Batch processing for direct debits, direct credits and one-off bank transfers | | **AU\_PCS** | Australia | Paper Clearing System | Settlement of cheques and other paper-based payments | \* indicates that direct debit mandates can be set up. ```json Request theme={null} { "creditorAccount": { "id": { "value": "ES2102390802941234566789", "type": "IBAN" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "BANKINTER SA", "address": { "addressLine1": "DON RAMON DE LA CRUZ 84", "addressLine3": "MADRID, MADRID, 28006", "department": "BANCA INTERNET", "streetName": "DON RAMON DE LA CRUZ", "buildingNumber": "84", "townName": "MADRID", "postCode": "28006", "countrySubdivision": "MADRID", "country": "ES" }, "bic": "BKBKESMMXXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/BKBKESMMXXX.png" }, "settlementInformation": { "settlementSystems": [ "SCT", "SDD_CORE", "SCT_INST", "SDD_B2B" ] } } } ``` ```json Request theme={null} { "country": "US", "creditorAgent": { "clearingSystemMemberId": { "memberId": "021000021" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "JPMORGAN CHASE BANK, NATIONAL ASSOCIATION", "address": { "addressLine1": "1111 POLARIS PARKWAY", "addressLine3": "COLUMBUS OH 43240, US", "department": "POLARIS PARKWAY", "streetName": "POLARIS PARKWAY", "buildingNumber": "1111", "townName": "COLUMBUS", "postCode": "43240", "countrySubdivision": "OHIO", "country": "US" }, "bic": "CHASUS33XXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/US_021000021.png" }, "settlementInformation": { "settlementSystems": [ "US_FEDACH", "US_FEDWIRE" ] } } } ``` ```json Request theme={null} { "country": "GB", "creditorAgent": { "clearingSystemMemberId": { "memberId": "230505" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "STRIPE PAYMENTS UK LTD", "address": { "addressLine1": "THE BOWER, 7TH FLOOR", "addressLine2": "211 OLD STREET", "addressLine3": "LONDON, EC1V 9NR, GREATER LONDON GB", "department": "STRIPE PAYMENTS UK LTD", "streetName": "OLD STREET", "buildingNumber": "211", "buildingName": "THE BOWER", "floor": "7TH FLOOR", "townName": "LONDON", "postCode": "EC1V 9NR", "countrySubdivision": "GREATER LONDON", "country": "GB" }, "bic": "STYAGB2LXXX" }, "settlementInformation": { "settlementSystems": [ "GB_BACS_CR", "GB_FPS" ] } } } ``` ```json Request theme={null} { "country": "AU", "creditorAgent": { "clearingSystemMemberId": { "memberId": "034134" } } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "WESTPAC BANKING CORPORATION", "address": { "addressLine1": "139 BOAT HARBOUR DRIVE", "addressLine3": "HERVEY BAY, QUEENSLAND, 4655", "department": "HERVEY BAY FINANCIAL CENTRE", "streetName": "BOAT HARBOUR DRIVE", "buildingNumber": "139", "townName": "HERVEY BAY", "postCode": "4655", "countrySubdivision": "QUEENSLAND", "country": "AU" }, "bic": "WPACAU2SXXX" }, "settlementInformation": { "settlementSystems": [ "AU_PCS", "AU_BECS", "AU_HVCS" ] } } } ``` ```json Request theme={null} { "country": "CA", "creditorAgent": { "clearingSystemMemberId": { "memberId": "001" }, "branchId": "07810" } } ``` ```json Response theme={null} { "enrichments": { "creditorAgent": { "name": "BANK OF MONTREAL", "address": { "addressLine1": "4729 MARINE AVE - POWELL RIVER", "addressLine2": "BRITISH COLUMBIA, V8A 2L3", "department": "WESTVIEW", "streetName": "MARINE AVE", "buildingNumber": "4729", "townName": "POWELL RIVER", "postCode": "V8A 2L3", "countrySubdivision": "BRITISH COLUMBIA", "country": "CA" }, "bic": "BOFMCAM2XXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/bank_of_montreal.png" }, "settlementInformation": { "settlementSystems": [ "CA_ACSS", "CA_LYNX" ] } } } ``` ## Bulk enrichment If you need to enrich multiple payments at once, use the bulk enrichment endpoint. Each item in the array follows the exact same schema as [POST /payments/enrichments](/api-reference/payment-preparation/enrich-payment-details). The response returns enrichment results in the same order as the input. The maximum number of items per request is **20**. [**POST** /payments/bulk-enrichments](/api-reference/payment-preparation/bulk-enrich-payment-details) ```json Request theme={null} { "items": [ { "creditorAgent": { "bic": "BARCUS33" } }, { "creditorAccount": { "id": { "value": "PT50002300004569522628094", "type": "IBAN" } } }, { "creditorAgent": { "bic": "CHASGB2L" }, "currency": "NZD" } ] } ``` ```json Response theme={null} { "items": [ { "enrichments": { "creditorAgent": { "name": "BARCLAYS BANK PLC", "address": { "addressLine1": "745 SEVENTH AVENUE", "addressLine3": "NEW YORK NY 10019, US", "streetName": "SEVENTH AVENUE", "buildingNumber": "745", "townName": "NEW YORK", "postCode": "10019", "countrySubdivision": "NEW YORK", "country": "US" }, "bic": "BARCUS33XXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/barclays.png" } } }, { "enrichments": { "creditorAgent": { "name": "BANCO ACTIVOBANK, SA", "address": { "addressLine1": "RUA AUGUSTA RC 84", "addressLine3": "LISBON, LISBON, 1100-053", "streetName": "RUA AUGUSTA RC", "buildingNumber": "84", "townName": "LISBON", "postCode": "1100-053", "countrySubdivision": "LISBON", "country": "PT" }, "bic": "ACTVPTPLXXX", "logo": "https://cdn.paysway.dev/assets/bank-logos/ACTVPTPLXXX.png" }, "settlementInformation": { "settlementSystems": [ "SCT", "SDD_CORE", "SCT_INST" ] } } }, { "enrichments": { "creditorAgent": { "name": "JPMORGAN CHASE BANK, N.A.", "address": { "addressLine1": "25 BANK STREET, CANARY WHARF", "addressLine3": "LONDON, E14 5JP, GREATER LONDON GB", "streetName": "BANK STREET", "buildingNumber": "25", "townName": "LONDON", "townLocationName": "CANARY WHARF", "postCode": "E14 5JP", "countrySubdivision": "GREATER LONDON", "country": "GB" }, "bic": "CHASGB2LXXX" }, "intermediaryAgent1": { "bic": "ANZBNZ22XXX" } } } ] } ``` # Overview Source: https://docs.paysway.io/payment-preparation/overview Bank transfers are difficult because of varying formats and requirements for each country, currency, and payment type. Payment Preparation API helps reduce this complexity by [validating](/payment-preparation/validate-payment) and [enriching](/payment-preparation/enrich-payment) full or partial payment instructions that you send to us as API requests. In response, we output clear validation alerts or additional data necessary to ensure smooth transactions and a better user experience. You likely already have multiple banking integrations with various partners and send them payment instructions in the [ISO 20022](https://www.iso20022.org/) messaging standard, which contain a number of different fields. * Payer information (name and account number, optionally address or other details like tax identifiers) * Currency and amount * Beneficiary details (same as for payer) * Beneficiary bank details (banking identifier of some type, like a BIC, or sort code, or routing number) * Routing inputs (intermediary BICs or type of the payment scheme) * Reference, purpose, fees type (optional) PaySway can consume the same details with minimal changes, because our API is inspired by the same standard that you use in your gateways. When you want to check the correctness of your user's input, you send a partial or full payment instruction to the [validation endpoint](/payment-preparation/validate-payment) as a request. If there are no issues in the input, you'll get an OK, else one or more alerts will highlight the exact reason. When you want to receive additional input to ensure payment success, you call our [enrichment endpoint](/payment-preparation/enrich-payment) with the same instruction as in **validation**. You can use the output of the enriched fields as required in your setup. Find the full list of supported corridors [here](https://docs.google.com/spreadsheets/d/1JpnDYhh65QL_KPsISVUKVH2nn79PdUOgY6_g6OhuF8Q/edit?gid=0#gid=0) # Validate payment Source: https://docs.paysway.io/payment-preparation/validate-payment ## Validate IBAN IBANs follow a specific algorithm to ensure that the chance of an error is minimized. You can query it individually or with other inputs [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "creditorAccount": { "id": { "value": "LT601010012345678901", "type": "IBAN" } } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ```json Request theme={null} { "creditorAgent": { "bic": "BARCGB00XXX" }, "creditorAccount": { "id": { "value": "GB33BARC2000625555555", "type": "IBAN" } } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_IBAN_INVALID_BBAN_STRUCTURE" }, { "code": "CREDITOR_BIC_DOES_NOT_EXIST" } ] } ``` ```json Request theme={null} { "creditorAccount": { "id": { "value": "BE57967950097279", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_IBAN_INCORRECT_CHECK_DIGITS" } ] } ``` ## Validate IBAN and BIC You might want to check that the IBAN (beneficiary's account) is from the right BIC (bank). It's helpful when you ask for both fields in the UI. Two or more BICs can be associated with a single IBAN (for example, REVOGB21 and REVOGB2L for GB85REVO04007549976222), and their use varies based on the currency and payment scheme. For EUR payments within the SEPA region, the optimal scheme is typically SEPA or SEPA Instant, and the correct BIC for this is REVOGB21. Using REVOGB2L will lead to payment failure or unintended routing through Target2 with intermediary fees. By default, we assume you prefer SEPA/SEPA Instant for eligible EUR transfers. Therefore, if you indicate that a transfer is in EUR, we will alert you if the BIC provided does not support SEPA/SEPA Instant. This alert won't be triggered if the currency is not specified. Outside of the SEPA context, we opt for softer consistency checks in order to compensate for potential issues in how banks share the account details with their customers. For example, if your user enters an AE IBAN and a BIC that corresponds to that bank's branch (like ABINAEAADXB) instead of the head entity (like ABINAEAAXXX), we won't return an alert. [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "creditorAgent": { "bic": "BARCGB22" }, "creditorAccount": { "id": { "value": "GB45CITI18502612467878", "type": "IBAN" } } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_BIC_IBAN_INCONSISTENCY" } ] } ``` ```json Request theme={null} { "creditorAgent": { "bic": "REVOGB2L" }, "creditorAccount": { "id": { "value": "GB85REVO04007549976222", "type": "IBAN" } } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ```json Request theme={null} { "creditorAgent": { "bic": "REVOGB2L" }, "creditorAccount": { "id": { "value": "GB85REVO04007549976222", "type": "IBAN" } }, "currency": "EUR" } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_BIC_SCHEME_UNSUPPORTED" } ] } ``` ```json Request theme={null} { "creditorAgent": { "bic": "LOYDGB2L" }, "currency": "KRW" } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_BIC_NO_SSI_FOR_CURRENCY" } ] } ``` ## Validate bank identifier When sending local transfers, you might want to ensure that the customer input for the bank identifier (BIC, sort code, ABA routing number,\ clearingnummer...) is correct. You need to clarify the country of that local bank identifier, so we know what you're dealing with. [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "country": "GB", "creditorAgent": { "clearingSystemMemberId": { "memberId": "040075" } } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ```json Request theme={null} { "country": "CA", "creditorAgent": { "clearingSystemMemberId": { "memberId": "001" }, "branchId": "12345" } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_BRANCH_IDENTIFIER_DOES_NOT_EXIST" } ] } ``` ```json Request theme={null} { "country": "HU", "creditorAgent": { "clearingSystemMemberId": { "memberId": "12600016" } } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ## Validate account number Some account numbers can be validated through specific algorithms. [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "country": "GB", "creditorAgent": { "clearingSystemMemberId": { "memberId": "040075" } }, "creditorAccount": { "id": { "value": "49976223", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_BANK_IDENTIFIER_ACCOUNT_NUMBER_INCONSISTENCY" } ] } ``` ```json Request theme={null} { "country": "AR", "creditorAccount": { "id": { "value": "0270056510000438550019", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_ACCOUNT_NUMBER_INCORRECT_CHECKSUM" } ] } ``` ```json Request theme={null} { "country": "MA", "creditorAccount": { "id": { "value": "350810000000000547371076", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ```json Request theme={null} { "country": "SE", "creditorAccount": { "id": { "value": "8937028383339", "type": "ACCOUNT_NUMBER" } } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ## Differences in corridor requirements When working with different providers in various corridors, there can be discrepancies in how they handle inputs. The most common variation occurs when there are two identifiers for the bank - such as a bank code and a branch code. Some providers expect separate fields for each identifier, while others require the two to be combined into a single string. Our API is designed to accommodate these differences and aims to support the well-known formatting patterns used by different providers. | Country | Possible UI fields | Supported patterns | | :------ | :--------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- | | Brazil | Bank code (`AAA`), branch code (`BBBB`) | - Concatenated: `clearingSystemMemberId: AAABBBB`
- Separated: `clearingSystemMemberId: AAA`, `branchId: BBBB` | | Canada | Institution number (`AAA`), transit number (`BBBBB`) | - Concatenated: `clearingSystemMemberId: AAABBBBB`
- Separated: `clearingSystemMemberId: AAA`, `branchId: BBBBB` | | Japan | Bank code (`AAAA`), branch code (`BBB`) | - Concatenated: `clearingSystemMemberId: AAAABBB`
- Separated: `clearingSystemMemberId: AAAA`, `branchId: BBB` | ## Validate chain of intermediaries When processing payments that involve an intermediary bank, you might want to ensure that the customer input for the intermediary BICs is correct and appropriate for the creditor agent and currency. By validating this information, we help confirm that the intermediary bank is suitable for the transaction, preventing potential routing errors. [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "intermediaryAgent1": { "bic": "CHASGB2LXXX" }, "creditorAgent": { "bic": "REVOGB2LXXX" }, "currency": "USD" } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ```json Request theme={null} { "intermediaryAgent1": { "bic": "CITIGB22XXX" }, "creditorAgent": { "bic": "REVOGB2LXXX" }, "currency": "USD" } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "INTERMEDIARY_1_NO_SSI" } ] } ``` ```json Request theme={null} { "intermediaryAgent1": { "bic": "IRVTUS3NXXX" }, "intermediaryAgent2": { "bic": "DEUTDEFFXXX" }, "creditorAgent": { "bic": "RVBKUZ22XXX" }, "currency": "USD" } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "INTERMEDIARY_2_NO_SSI" } ] } ``` ## Validate settlement system You can validate that a bank identifier supports the specified settlement system. Different banks may support different settlement systems, and validating this compatibility helps prevent payment failures caused by routing through unsupported settlement methods. [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "country": "US", "creditorAgent": { "clearingSystemMemberId": { "memberId": "011001726" } }, "settlementInformation": { "settlementSystem": "US_FEDWIRE" } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_BANK_IDENTIFIER_SCHEME_UNSUPPORTED" } ] } ``` ## Validate document identifiers In some corridors, the settlement scheme or your payment provider may require you to specify the creditor's personal identifier - such as a tax ID or passport number. You can validate these identifiers before submitting a payment. We validate format and, where applicable, checksum for well-known document types. Some corridors have well-known formatting patterns using special symbols (e.g. Brazilian CPF - `231.002.999-00`). We respect those formatting patterns and validate both `231.002.999-00` and `23100299900` in the same way. [**POST** /payments/validations](/api-reference/payment-preparation/validate-payment-details) ```json Request theme={null} { "country": "BR", "creditor": { "documents": [ { "type": "CPF", "value": "60735047480" } ] } } ``` ```json Response theme={null} { "status": "OK", "alerts": [] } ``` ```json Request theme={null} { "country": "CO", "creditor": { "documents": [ { "type": "NIT", "value": "700522594" } ] } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_DOCUMENT_1_INVALID_FORMAT" } ] } ``` ```json Request theme={null} { "country": "AR", "creditor": { "documents": [ { "type": "CUIT", "value": "27316952429" } ] } } ``` ```json Response theme={null} { "status": "ALERT", "alerts": [ { "code": "CREDITOR_DOCUMENT_1_INCORRECT_CHECKSUM" } ] } ``` **Supported document types by corridor** | Country | Document types | | :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Argentina | **CUIT** - Clave Única de Identificación Tributaria
**CUIL** - Código Único de Identificación Laboral | | Brazil | **CPF** - Cadastro de Pessoas Físicas
**CNPJ** - Cadastro Nacional da Pessoa Jurídica | | Chile | **RUT** - Rol Único Tributario | | China | **CUI** - Resident Identity Card
**PASS** - Passport
**TAXID** - Tax ID | | Colombia | **CC** - Cédula de Ciudadanía
**NIT** - Número de Identificación Tributaria
**CE** - Carnet de Extranjería
**PASS** - Pasaporte
**PEP** - Permiso Especial de Permanencia | | Costa Rica | **CI** - Cédula de Identidad
**CJ** - Cédula Jurídica
**CR** - Cédula de Residencia | | Ecuador | **CI** - Cédula de Identidad
**RUC** - Registro Único de Contribuyentes
**PASS** - Pasaporte | | Panama | **CI** - Cédula de Identidad
**RUC** - Registro Único de Contribuyentes
**PASS** - Pasaporte | | Paraguay | **CI** - Cédula de Identidad
**RUC** - Registro Único de Contribuyentes | | Peru | **DNI** - Documento Nacional de Identidad
**RUC** - Registro Único de Contribuyentes
**CE** - Carnet de Extranjería
**PASS** - Pasaporte | | Uruguay | **CI** - Cédula de Identidad
**RUT** - Registro Único Tributario
**DE** - Documento Extranjero
**PASS** - Pasaporte | ## Bulk validation If you need to validate multiple payments at once, use the bulk validation endpoint. Each item in the array follows the exact same schema as [POST /payments/validations](/api-reference/payment-preparation/validate-payment-details) and produces the same [alert codes](#alert-codes). The response returns validation results in the same order as the input. The maximum number of items per request is **20**. [**POST** /payments/bulk-validations](/api-reference/payment-preparation/bulk-validate-payment-details) ```json Request theme={null} { "items": [ { "creditorAccount": { "id": { "value": "LT601010012345678901", "type": "IBAN" } } }, { "creditorAgent": { "bic": "BARCGB00XXX" }, "creditorAccount": { "id": { "value": "GB33BARC2000625555555", "type": "IBAN" } } }, { "country": "BR", "creditor": { "documents": [ { "type": "CPF", "value": "60735047480" } ] } } ] } ``` ```json Response theme={null} { "items": [ { "status": "OK", "alerts": [] }, { "status": "ALERT", "alerts": [ { "code": "CREDITOR_IBAN_INVALID_BBAN_STRUCTURE" }, { "code": "CREDITOR_BIC_DOES_NOT_EXIST" } ] }, { "status": "OK", "alerts": [] } ] } ``` ## Alert codes Here's the list of the alerts with explanations, which you can use in UI localization. In some cases, there can be more than one alert code in our response. | Code | Explanation | Example | | :----------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | *CREDITOR\_IBAN\_INVALID\_FORMAT* | The IBAN doesn't follow the correct format or structure. | `GT01` | | *CREDITOR\_IBAN\_INVALID\_COUNTRY\_CODE* | The IBAN country code is not a valid ISO 3166-1 alpha-2 code. | `XY02` | | *CREDITOR\_IBAN\_COUNTRY\_NOT\_SUPPORTED* | The entered country does not support IBAN standards. | `US03` | | *CREDITOR\_IBAN\_INVALID\_BBAN\_STRUCTURE* | The IBAN's BBAN has an incorrect length or contains invalid characters. | `GB01DEEL999LETSGO` | | *CREDITOR\_IBAN\_INCORRECT\_CHECK\_DIGITS* | The IBAN is incorrect due to check digits. | `LT043250045821439861` | | *CREDITOR\_IBAN\_COUNTRY\_INCONSISTENCY* | The IBAN does not match the selected bank country. | `GB85REVO04007549976222` for US | | *CREDITOR\_IBAN\_NO\_SSI\_FOR\_CURRENCY* | The IBAN does not support selected currency. | `BRL` to `LT583250045821439861` | | *CREDITOR\_BIC\_INVALID\_FORMAT* | The BIC does not match the correct format. | `BOFAUS` | | *CREDITOR\_BIC\_INVALID\_COUNTRY\_CODE* | The BIC's country code is not a valid ISO 3166-1 alpha-2 code. | `BOFAXX33` | | *CREDITOR\_BIC\_DOES\_NOT\_EXIST* | The BIC cannot be found in official bank listings - it might be outdated or incorrect. | `DEELUS99` | | *CREDITOR\_BIC\_IBAN\_INCONSISTENCY* | The IBAN and BIC entered are not consistent. Another BIC must be used. | `BARCGB2L` for `GB85REVO04007549976222` | | *CREDITOR\_BIC\_SCHEME\_UNSUPPORTED* | The BIC does not support the required payment scheme for the transfer. | `REVOGB2L` for `GB85REVO04007549976222` (should be `REVOGB21`, as SEPA is preferred to any other payment scheme) | | *CREDITOR\_BIC\_NO\_SSI\_FOR\_CURRENCY* | The BIC does not support selected currency. | `KRW` to `LOYDGB2L` | | *CREDITOR\_BIC\_COUNTRY\_INCONSISTENCY* | The BIC does not match the selected country. | `NDEASESSXXX` for CH | | *CREDITOR\_BIC\_COMPLIANCE\_HIT* | The BIC is restricted for compliance reasons. | `HAVIGB2LXXX` | | *CREDITOR\_BANK\_IDENTIFIER\_INVALID\_FORMAT* | The bank identifier doesn't follow the correct format for this country. | `0400755` for GB | | *CREDITOR\_BANK\_IDENTIFIER\_DOES\_NOT\_EXIST* | The local bank identifier entered does not seem to be present in published bank lists. It could be wrong, deprecated, or has yet to appear. | `040123` for GB | | *CREDITOR\_BRANCH\_IDENTIFIER\_INVALID\_FORMAT* | The branch identifier doesn't follow the correct format for this country. | `1234` for JP | | *CREDITOR\_BRANCH\_IDENTIFIER\_DOES\_NOT\_EXIST* | The branch identifier entered does not seem to be present in published lists for the bank. It could be wrong, deprecated, or has yet to appear. | `999` for Mizuho Bank (JP) | | *CREDITOR\_ACCOUNT\_NUMBER\_INVALID\_FORMAT* | The account number does not match the valid format for this country. | `123456789` for GB | | *CREDITOR\_ACCOUNT\_NUMBER\_TOO\_SHORT* | The account number you entered is too short. It may be truncated, have missing zeroes, or missing the bank identifier. | `947093420` for SE | | *CREDITOR\_ACCOUNT\_NUMBER\_TOO\_LONG* | The account number you entered is too long. It may contain extra digits. | `135000144454` for SE | | *CREDITOR\_ACCOUNT\_NUMBER\_INCORRECT\_CHECKSUM* | The account number fails the checksum validation. | `0270056410000438550018` for AR | | *CREDITOR\_ACCOUNT\_NUMBER\_INVALID\_BANK\_IDENTIFIER* | The account number contains an invalid bank identifier. | `0280056110000438550019` for AR (as 028 is not valid bank identifier) or `10011234567` for SE (1001 is not a valid clearingnummer) | | *CREDITOR\_ACCOUNT\_NUMBER\_POSSIBLE\_CARD\_NUMBER* | The account number appears to be a valid bank card number (passes the Luhn check). | `4111111111111111` for KE | | *CREDITOR\_ACCOUNT\_ID\_EMAIL\_INVALID\_FORMAT* | The provided email address doesn't follow the correct format. | `user@domain` or `user.domain.tld` | | *CREDITOR\_ACCOUNT\_ID\_MSISDN\_INVALID\_FORMAT* | The provided mobile number doesn't follow the correct format | `+12345` or `0011111111` | | *CREDITOR\_BANK\_IDENTIFIER\_ACCOUNT\_NUMBER\_INCONSISTENCY* | The account number and bank identifier combination is incorrect. | `040075` and `49976223` for GB | | *CREDITOR\_BANK\_IDENTIFIER\_ACCOUNT\_NUMBER\_SWAP* | The account number is an actual bank identifier | For US, `021000021` as account number | | *CREDITOR\_BANK\_IDENTIFIER\_ACCOUNT\_NUMBER\_DUPLICATION* | The bank identifier and account number are duplicated | For US, `021000021` as both account number and routing number | | *CREDITOR\_BANK\_IDENTIFIER\_SCHEME\_UNSUPPORTED* | The bank identifier is not compatible with the specified settlement scheme | `011001726` as routing number and `US_FEDWIRE` as settlement system | | *CREDITOR\_DOCUMENT\_1\_INVALID\_FORMAT* | The creditor document doesn't follow the correct format for this country and document type. | `700522594` as NIT for CO (expected 10 digits) | | *CREDITOR\_DOCUMENT\_1\_INCORRECT\_CHECKSUM* | The creditor document fails checksum validation. | `27316952429` as CUIT for AR | | *INTERMEDIARY\_1\_INVALID\_FORMAT* | The intermediary BIC you entered is of invalid format | `BOFAUS` | | *INTERMEDIARY\_1\_DOES\_NOT\_EXIST* | The intermediary BIC you entered does not exist. | `CHASUSMMXXX` | | *INTERMEDIARY\_1\_NO\_SSI* | The intermediary BIC does not have standing settlement instructions for this currency with the next agent in the chain. | `USD` to `REVOGB2LXXX` through `CITIGB22XXX` | | *INTERMEDIARY\_1\_COMPLIANCE\_HIT* | The intermediary BIC is restricted for compliance reasons. | `HAVIGB2LXXX` | | *INTERMEDIARY\_2\_INVALID\_FORMAT* | The intermediary BIC you entered is of invalid format | `BOFAUS` | | *INTERMEDIARY\_2\_DOES\_NOT\_EXIST* | The intermediary BIC you entered does not exist. | `CHASUSMMXXX` | | *INTERMEDIARY\_2\_NO\_SSI* | The intermediary BIC does not have standing settlement instructions for this currency with the next agent in the chain. | `USD` to `RVBKUZ22XXX` through `IRVTUS3NXXX` and `SCPEUZ22XXX` | | *INTERMEDIARY\_2\_COMPLIANCE\_HIT* | The intermediary BIC is restricted for compliance reasons. | `HAVIGB2LXXX` | | *INTERMEDIARY\_3\_INVALID\_FORMAT* | The intermediary BIC you entered is of invalid format | `BOFAUS` | | *INTERMEDIARY\_3\_DOES\_NOT\_EXIST* | The intermediary BIC you entered does not exist. | `CHASUSMMXXX` | | *INTERMEDIARY\_3\_NO\_SSI* | The intermediary BIC does not have standing settlement instructions for this currency with the next agent in the chain. | `USD` to `REVOGB21XXX` through `CHASUS33XXX`, `CHASGB2LXXX` and `REVOLT21XXX` | | *INTERMEDIARY\_3\_COMPLIANCE\_HIT* | The intermediary BIC is restricted for compliance reasons. | `HAVIGB2LXXX` | # Event types Source: https://docs.paysway.io/webhooks/event-types PaySway webhook events are organized by model types, where each event type follows the format `MODEL.ACTION`. This structure helps you understand which part of the system triggered the event and what action occurred. ## Available events | Model type | Event types | | ----------- | ------------------------------------------------------------------------------------------------------------------------------- | | `RECIPIENT` | [`RECIPIENT_CREATED`](#recipient-created), [`RECIPIENT_UPDATED`](#recipient-updated), [`RECIPIENT_DELETED`](#recipient-deleted) | ## Event types by model The following sections document each event type. Each event object is nested within the standard webhook [payload structure](/webhooks/implement-handler#payload-structure), with unique data in the `object` field containing event-specific information. ### RECIPIENT Events related to Beneficiary Hub recipient management and lifecycle operations. Triggered when an end-user successfully adds a new recipient. For detailed field definitions, see [API Reference](/api-reference/beneficiary-hub/recipient-events). ```json Example theme={null} { "id": "ed110f81-efab-434a-ba5f-0efcf5f11eeb", "externalUserId": "c6fd6330-6932-4678-abf8-600fe155909c" } ``` Triggered when an end-user modifies any recipient information, including setting a new default recipient. For detailed field definitions, see [API Reference](/api-reference/beneficiary-hub/recipient-events). ```json Example theme={null} { "id": "ed110f81-efab-434a-ba5f-0efcf5f11eeb", "externalUserId": "d937f855-de7a-49dc-99ca-7d5f9f1f39e2" } ``` Triggered when an end-user removes a recipient. For detailed field definitions, see [API Reference](/api-reference/beneficiary-hub/recipient-events). ```json Example theme={null} { "id": "ed110f81-efab-434a-ba5f-0efcf5f11eeb", "externalUserId": "a70ea3bb-c677-4af5-9ee3-ff11f96cb433" } ``` # Implement handler Source: https://docs.paysway.io/webhooks/implement-handler When you build a webhook endpoint to receive events from PaySway, your handler must be prepared for both successful and failed deliveries, as well as the structure of the JSON payload that arrives with each event. Below are key considerations and technical details to help you implement a robust endpoint. ## Delivery and retry logic PaySway delivers events one at a time, sending each as a separate HTTP request to your webhook endpoint. Here's how delivery and retries work: Each HTTP request contains exactly one event in JSON format. If your handler processes an event successfully, it should return a `200 OK` response status. This indicates that PaySway can safely mark the event as delivered. If your endpoint returns any status other than `200 OK`, the delivery is considered failed. PaySway will retry delivering that event using an exponential backoff strategy. There is no limit on the number of retry attempts, so your handler should be idempotent and prepared for possible repeated messages. Failed deliveries for one event do not prevent PaySway from sending subsequent events. Each event is processed independently in its own request. As soon as a `200 OK` response is received, PaySway treats the event as delivered and will not attempt to send it again. ## Payload structure PaySway packages each event in a single JSON payload with metadata and event-specific data: A unique identifier for the event. Use this for idempotency checks. The date and time the event was created, formatted as ISO 8601. The type of event that occurred. See [event types](/webhooks/event-types) for all available values. A structured event object containing event-specific data. The schema varies by event type. ### Example payload ```json theme={null} { "id": "45239619-07eb-47c7-9a58-f98650c269ba", "createdDate": "2025-08-22T12:54:14.418745Z", "eventType": "RECIPIENT_CREATED", "object": { "id": "3404bd69-c7ca-4ce0-8a65-358cbf2ee32f", "externalUserId": "c6fd6330-6932-4678-abf8-600fe155909c" } } ``` # Overview Source: https://docs.paysway.io/webhooks/overview When integrating with PaySway, you often need your applications to respond automatically whenever important events occur. This is where webhooks come into play. Instead of constantly polling for updates, you can rely on PaySway to send notifications to your systems in real time whenever new activity happens. ## How webhooks work A webhook is a secure HTTPS endpoint that you host within your backend. When events occur in PaySway, we send HTTPS requests containing JSON payloads to your registered endpoints. Each payload includes information about what happened and when. Receive instant updates when events occur, enabling immediate response and processing. Built-in retry logic with exponential backoff ensures events reach your systems even during temporary outages. Cryptographic signatures ensure only authentic requests from PaySway reach your endpoints. Subscribe only to the events you need, reducing noise and processing overhead. ## Implementation steps Follow our step-by-step guide to implement webhooks in your application: Create a secure HTTPS endpoint that can receive and process PaySway event notifications. Learn about requirements, retry logic, and proper response handling. Configure your webhook subscription through the PaySway API to start receiving events. Set up subscriptions, manage event filters, and obtain security credentials. Implement signature verification to ensure requests are genuinely from PaySway. Secure your webhook with cryptographic signature verification. Complete reference documentation for all webhook event types and their data structures. # Subscribe to events Source: https://docs.paysway.io/webhooks/subscribe-to-events PaySway manages webhooks through **webhook subscriptions**, each of which has its own callback URL, event filter, and other optional metadata to help you organize and secure your integrations. This guide shows you how to create and manage webhook subscriptions. ## Create a webhook subscription To register a webhook subscription, send the following request describing your new webhook: [**POST** /webhooks/subscriptions](/api-reference/webhooks/create-webhook-subscription) **Request parameters:** The label to help you distinguish one webhook from another. The HTTPS endpoint URL where events are delivered. Important constraints: * **URL Scheme:** Only HTTPS is supported * **Host Requirement:** The host portion must be a valid domain name (IP addresses are not allowed) * **No Redirects:** Your endpoint must not respond with HTTP `3xx` codes The list of event types you want to receive webhooks for. Providing an empty event types list means no events will be delivered to that subscription. Whether the subscription should be enabled immediately. Defaults to `true`. ```json Request theme={null} { "title": "Recipients webhook", "enabled": true, "url": "https://tenant.tld/webhooks", "events": [ "RECIPIENT_CREATED", "RECIPIENT_UPDATED", "RECIPIENT_DELETED" ] } ``` ```json Response theme={null} { "id": "0d4373d7-f2d4-4f30-8660-c53109a38a9c", "createdDate": "2025-09-19T11:19:55.194333Z", "title": "Recipients webhook", "enabled": true, "url": "https://tenant.tld/webhooks", "events": [ "RECIPIENT_CREATED", "RECIPIENT_UPDATED", "RECIPIENT_DELETED" ], "secret": "H6lXEzR+0mt3PBrVvp4N6cTUYOJCM60Frk1qpmx4vXM=" } ``` **Response fields:** Unique identifier for the webhook subscription. The date and time the subscription was created, formatted as ISO 8601. The title you provided for the subscription. Whether the subscription is currently enabled and will receive events. The endpoint URL where events will be delivered. List of event types this subscription will receive. The signing secret for verifying webhook requests. This is only returned once at creation time. The `secret` field is critical for [verifying signatures of webhook requests](/webhooks/verify-requests). This secret is only returned once at the time of creation, so be sure to save it in a secure location. If you lose the secret, you will need to create a new subscription. ## Enable and disable subscriptions You can control whether a subscription actively receives events using the `enabled` field. Webhook subscriptions are created enabled by default and will immediately start receiving events unless you explicitly set `enabled: false` during creation. Set `enabled: false` in your creation request to create a subscription that won't immediately start receiving events. [**POST** /webhooks/subscriptions](/api-reference/webhooks/create-webhook-subscription) ```json Request theme={null} { "title": "Test webhook", "enabled": false, "url": "https://tenant.tld/webhooks", "events": ["RECIPIENT_CREATED"] } ``` Use a `PATCH` request to enable the subscription when your handler is ready to process incoming requests. [**PATCH** /webhooks/subscriptions/\{subscriptionId}](/api-reference/webhooks/update-webhook-subscription) ```json Request theme={null} { "enabled": true } ``` # Verify requests Source: https://docs.paysway.io/webhooks/verify-requests PaySway signs each webhook request by computing an HMAC-SHA256 hash of the raw request body concatenated with a timestamp. This hash is generated using a secret value that only you and PaySway know. The resulting signature is provided in the `X-PaySway-Signature` header. By replicating this same process with your secret, you can confirm that the webhook request is authentic and has not been tampered with. When you [create a webhook subscription](/webhooks/subscribe-to-events), the response includes a `secret` field that is base64-encoded. You must decode this string before using it to generate an HMAC signature. ```javascript JavaScript theme={null} const base64Secret = "zTOJGr3vYdAHM/F5ZiDsVvgPZq5/Y3Ktbo9xw9Ncf8Y="; const decodedSecret = Buffer.from(base64Secret, "base64"); ``` ```java Java theme={null} import java.util.Base64; // ... String base64Secret = "zTOJGr3vYdAHM/F5ZiDsVvgPZq5/Y3Ktbo9xw9Ncf8Y="; byte[] decodedSecret = Base64.getDecoder().decode(base64Secret); ``` PaySway includes an `X-PaySway-Signature` header in each webhook request. This header contains two key-value pairs separated by commas: * `t`: The UNIX timestamp of when the message was signed * `v1`: The actual signature in hexadecimal format **Example Header** ```text Signature header theme={null} X-PaySway-Signature: t=1738002855,v1=c9854765d242b9078e68b6fca1755f208ba70a7aa7c372abc4ec341483e34496 ``` Parse the header to extract the `t` and `v1` values. Ignore any other values that may appear in the header. ```javascript JavaScript theme={null} const signatureHeader = "t=1738002855,v1=c9854765d242b9078e68b6fca1755f208ba70a7aa7c372abc4ec341483e34496"; const segments = signatureHeader.split(","); const timestamp = segments.find(s => s.startsWith("t=")).substring(2); const signature = segments.find(s => s.startsWith("v1=")).substring(3); ``` ```java Java theme={null} import java.util.Arrays; // ... String signatureHeader = "t=1738002855,v1=c9854765d242b9078e68b6fca1755f208ba70a7aa7c372abc4ec341483e34496"; String[] segments = signatureHeader.split(","); String timestamp = Arrays.stream(segments) .filter(s -> s.startsWith("t=")) .map(s -> s.substring(2)) .findFirst() .orElseThrow(); String signature = Arrays.stream(segments) .filter(s -> s.startsWith("v1=")) .map(s -> s.substring(3)) .findFirst() .orElseThrow(); ``` PaySway signs the combination of the timestamp and raw request body, separated by a period ```javascript JavaScript theme={null} const rawBody = '{"foo":"bar"}'; const signingPayload = `${timestamp}.${rawBody}`; ``` ```java Java theme={null} String rawBody = "{\"foo\":\"bar\"}"; String signingPayload = timestamp + "." + rawBody; ``` Do not parse or modify the request body before verification. Use the raw, unmodified payload exactly as received, preserving all whitespace and formatting. Use your webhook secret to compute the HMAC-SHA256 hash of the signing payload. Convert the resulting hash to a hexadecimal string for comparison. ```javascript JavaScript theme={null} import { createHmac } from "crypto"; // ... const expectedSignature = createHmac("sha256", decodedSecret) .update(signingPayload, "utf8") .digest("hex"); ``` ```java Java theme={null} import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; // .. Mac sha256 = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(decodedSecret, "HmacSHA256"); sha256.init(secretKey); byte[] hash = sha256.doFinal(signingPayload.getBytes("UTF-8")); StringBuilder expectedSignatureBuilder = new StringBuilder(); for (byte b : hash) { expectedSignatureBuilder.append(String.format("%02x", b)); } String expectedSignature = expectedSignatureBuilder.toString(); ``` Compare your `expectedSignature` with the `v1` value from the `X-PaySway-Signature` header: * **If they match**: The request is authentic and was signed by PaySway using the correct secret * **If they don't match**: Reject the request as potentially malicious or corrupted Additionally, use the timestamp `t` to implement replay attack protection by setting a maximum acceptable age for requests (e.g., 5 minutes).