diff --git a/account/adding-safe-owner.mdx b/account/adding-safe-owner.mdx deleted file mode 100644 index 49b5d5e..0000000 --- a/account/adding-safe-owner.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: "Managing Safe Ownership" -description: "Add a new owner to a Gnosis Pay Safe Account" ---- - -Adding a new owner to a Gnosis Pay Safe registers that account as a **Delay Module owner**. -Only Delay Module owners can approve and execute on-chain transactions. - - -**Important distinction:** -- **Authenticated wallets** are linked to a Gnosis Pay user for API access (via SIWE and JWT). -- **Safe owners (Delay Module owners)** are accounts explicitly added on-chain to the Safe. -Authenticated wallets do **not** become Safe owners unless they are also as owners for the safe. - - -This guide walks through the process of adding an additional Safe owner. Once complete, the new owner will be able to approve and execute transactions through the Delay Module. - - - - Follow the [authentication flow](https://docs.gnosispay.com/auth) to obtain a JWT token for the Gnosis Pay user. - - - - Call [this endpoint](https://docs.gnosispay.com/api-reference/safe-owners/get-typed-data-for-adding-a-new-safe-owner) to receive the typed data. - Sign the typed data with the current Safe owner’s key to produce a signature. - - ```bash - curl --request GET \ - --url https://api.gnosispay.com/api/v1/owners/add/transaction-data \ - --header "Authorization: Bearer " - ``` - - - - Send the new owner’s address, the signature, and the signed message to [this endpoint](https://docs.gnosispay.com/api-reference/safe-owners/add-a-new-owner-to-the-safe). - - ```bash - curl --request POST \ - --url https://api.gnosispay.com/api/v1/owners \ - --header "Authorization: Bearer " \ - --header "Content-Type: application/json" \ - --data '{ - "newOwner": "0x3270bf32AB647e90eF94A026c70Aa1daaaDA2382", - "signature": "0x1234567890abcdef...", - "message": { - "salt": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - "data": "0xa9059cbb0000000000000000000000003270bf32ab647e90ef94a026c70aa1daaaada2382" - } - }' - ``` - The operation is processed through the Delay Relay and executes after a 3-minute delay. - - - - Confirm that the new owner was added to the Delay Module by checking the [Safe’s owner list](https://docs.gnosispay.com/api-reference/safe-owners/get-the-list-of-safe-owners). - - ```bash - curl --request GET \ - --url https://api.gnosispay.com/api/v1/owners \ - --header "Authorization: Bearer " - ``` - - diff --git a/account/index.mdx b/account/index.mdx deleted file mode 100644 index 7bfb23d..0000000 --- a/account/index.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: "Accounts on Gnosis Pay" -description: "How the Gnosis Pay Safe works" ---- - -When a user signs up for Gnosis Pay, a new **Gnosis Pay Safe** a smart contract wallet is created on-chain. -This Safe holds the user’s funds and is controlled only by its **owners**. - -A Gnosis Pay Safe owner can be either: -- an **EOA (Externally Owned Account)**, or -- another **smart wallet** (e.g. a Safe) whose owners may use passkeys, EOAs, or other signers. - -Funds in a Gnosis Pay Safe are never held by Gnosis Pay or any third party; ownership and control remain entirely with the Safe owners. - - - - - ---- - -## Authenticating sessions with wallets - -To access the Gnosis Pay dashboard or APIs, a wallet must authenticate using [Sign-In With Ethereum (SIWE)](https://docs.login.xyz/). -After SIWE, a JWT token is issued and used to authorize API calls (e.g. ordering cards, fetching user info, or linking additional authenticated wallets). - -Authenticated wallets are used **only for login and API sessions**. They do not perform on-chain actions and may or may not overlap with the Safe owners. - - -The first wallet connected during setup is automatically registered as both an **authenticated wallet** and the **initial Safe owner** (via the Delay Module). - - -Follow the guide on [adding an authenticated wallet](account/update-authenticated-account) for step-by-step instructions. - ---- - -## Safe Smart Account - -Each Gnosis Pay user is assigned a **Safe Smart Account**, a self-custodied smart contract wallet deployed on Gnosis Chain (L1). -Currently, a new Safe is deployed for every user. Support for connecting an existing Safe multisig is planned. - -### What is a smart contract wallet? - -Smart contract wallets are contracts that manage assets on-chain with programmable rules. -They enable advanced security and automation beyond EOAs. - -[Safe](https://safe.global/) is the leading smart contract wallet, securing over $70B in assets. - -### Exploring the Safe setup - -Users can view their Safe configuration in the [Safe web app](https://app.safe.global/) via the Zodiac app: - -1. Open the Gnosis Pay Dashboard. -2. Click **“View all transactions here”** to open the Safe web app. -3. In the sidebar, select **Applications**. -4. Search for **Zodiac**. - -The **Roles Module** and **Delay Module** are open-source contracts licensed under LGPL 3.0. - ---- - -## Modules - -Modules extend Safe functionality with custom logic. -In Gnosis Pay, modules enforce spending rules and transaction flows while keeping user funds in self-custody. - -These modules follow the [Zodiac standard](https://gnosisguild.org/zodiac/) developed by Gnosis Guild. - ---- - -## Roles Module - -The Roles Module enforces which actions Gnosis Pay can perform on behalf of the user. It defines: - -1. **Token used** → which token Gnosis Pay can spend. -2. **Daily limit** → maximum amount spendable per day. -3. **Recipient address** → destination for allowed transfers (e.g. the issuer’s settlement Safe). -4. **Role delegation** → assigns these permissions to Gnosis Pay. - -Only Safe owners can update these rules. -For example, a user may adjust the daily spending limit at any time. - ---- - -## Delay Module - -The Delay Module enforces a **3-minute delay** for all non-card transactions (e.g. transfers, deposits). -This ensures funds remain available for card payments and prevents race conditions. - - -During the 3-minute delay, the card is paused if any non-card transactions are pending. - - -The Delay Module also ensures the user’s EOA retains ultimate control. -During activation, the EOA becomes an indirect Safe owner via the Delay Module. - -⚠️ Reconfiguring or removing the Delay Module may prevent Gnosis Pay from functioning properly. - ---- - -## What happens if I lose access to my EOA Wallet? - -If you’ve forgotten the password to your EOA Wallet, you can regain access using its seed phrase. - - -However, if you’ve lost access to the seed phrase and cannot regain access to the EOA, you will also lose access to the Safe if it’s the only owner. - - -**To mitigate this risk, consider using a [Safe](https://safe.global/) with multiple owners instead of an EOA.** \ No newline at end of file diff --git a/account/update-authenticated-account.mdx b/account/update-authenticated-account.mdx deleted file mode 100644 index 347b59d..0000000 --- a/account/update-authenticated-account.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: "Adding a new wallet for authentication" -description: "Link an additional wallet address to a Gnosis Pay user" ---- - -A Gnosis Pay user can have multiple authenticated wallets attached to their account. -Authenticated wallets are used to sign in via SIWE and to start new API sessions. - - - - Follow the [authentication flow](https://docs.gnosispay.com/auth) to obtain a JWT token for the Gnosis Pay user. - - - - Make a POST call to [this endpoint](https://docs.gnosispay.com/api-reference/account-management/create-a-new-eoa-account-for-the-current-user) with the address to add. - - ```bash - curl --request POST \ - --url https://api.gnosispay.com/api/v1/eoa-accounts \ - --header "Authorization: Bearer " \ - --header "Content-Type: application/json" \ - --data '{ - "address": "0x1234567890abcdef1234567890abcdef12345678" - }' - ``` - - - - Call the [list accounts endpoint](https://docs.gnosispay.com/api-reference/account-management/retrieve-the-eoa-accounts-for-the-current-user). - The new address should appear in the response. - - ```bash - curl --request GET \ - --url https://api.gnosispay.com/api/v1/eoa-accounts \ - --header "Authorization: Bearer " - ``` - - - - -- Adding an **authenticated wallet** only links it to a Gnosis Pay user for login and API sessions. -- An authenticated wallet is **not automatically a Safe owner**. -- Only Safe owners (EOAs or smart wallets added to the Safe) can interact with the Delay Module or execute on-chain actions. -- Authenticated wallets can use the API (e.g. place card orders, fetch user info) but **cannot control funds** unless also added as Safe owners. - diff --git a/architecture.mdx b/architecture.mdx new file mode 100644 index 0000000..9827533 --- /dev/null +++ b/architecture.mdx @@ -0,0 +1,65 @@ +--- +title: "Gnosis Pay Architecture" +description: "How onboarding, spending, and settlement flow through Gnosis Pay" +--- + +Gnosis Pay is the orchestration layer that connects KYC flows, self custody of funds through safe account, KYT/AML checks on funds transfer, payment processing, and settlement. The diagram below shows how each system plugs in. + +Gnosis Pay architecture + + +The diagram reads in two directions. From the top down, it shows how a **partner** integrates. From the user's journey through the middle and bottom, it shows what an **end user** experiences. + +## For partners + +You integrate with Gnosis Pay infrastructure and get assigned a TenantID (also referred as PartnerID,which is an idenitifier for you as a partner) and allows us to spin up dedicated credentials to access userbase. + +Partner infrastructure has three components: + + + + Onboard your users end-to-end wallet sign-in with SIWE, email and TOS, and KYC. You can drive the full flow through the API or bring your own onboarding. If you have Sumsub instance, you can also perform KYC sharing + + + Subscribe to real-time events to keep your systems in sync and power notifications to your users. + + + Manage your program, support your users (B2C), and access analytics and user data — so you own the relationship directly. + + + A partner-hosted secure element for displaying sensitive card details (PAN, CVV, expiry) on your own domain. Authenticated with mTLS. + + + + +Gnosis Pay as the orchestration layer exposes three domains: + +- **Account** : create and manage user accounts, balances, and limits. +- **Card**: issue and control virtual cards, card limits and control. +- **Processing + Settlement**: authorization, transaction processing, and settlement across the payment rails. We also handle chargebacks and disputes. + + +## For users + +From the end user's point of view, the platform moves them from sign-in to spending in a single continuous flow. + + + + The user connects their wallet with SIWE (Sign-In with Ethereum), verifies their email, and accepts the terms of service. + + + The user completes KYC through Sumsub. Once verified and approved, their KYC is to be linked with user account. + + + Gnosis Pay provisions the user's account with Pismo and deploys their **Spending Safe** — a self-custodial Safe where the user's funds are held. A card is issued against the account. During this deployment, we also set the user's spendable currency and primary token that is allowed to be spent from user's safe. + + + The user deposits funds into their Spending Safe. Every deposit is screened by **KYT** (AML) before the funds become spendable.Because the Spending Safe is self-custodial, the user keeps control of their funds throughout deposits, balances, and withdrawals. + + + When the user pays with their card, the transaction is authorized in real time against the account balance and balance is moved accordingly on ledger. + + + Spending is settled through Monavate on a 12-hour batch. + + diff --git a/auth.mdx b/auth.mdx deleted file mode 100644 index 789380f..0000000 --- a/auth.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Authentication Flow -description: Use Sign-In with Ethereum (SIWE) to authenticate API requests. ---- - -Our authentication system uses Sign-In with Ethereum (SIWE) to generate a `jwt` to interact with our API. -This means you don't need to handle the storage of a powerful API key or spend hours configuring granular permissions. - - -**For Partners**: If you're building a partner integration, make sure to register your domains through the [Partners Dashboard](https://partners.gnosispay.com/) during signup. This ensures your domains are whitelisted for SIWE authentication and prevents authentication failures. - - - -## Authentication Process - - - -Before initiating authentication, [the application must request a nonce](/api-reference/authentication/generate-nonce). -Your application then presents this nonce to the user for signing as part of the SIWE flow. - -```bash cURL -curl -X GET /api/v1/auth/nonce -``` - - -Our API accepts signatures from Externally Owned Accounts (EOAs) and Smart Accounts (EIP-1271). - - -The signer must be an owner of a Gnosis Pay account. -Non-registered users will receive `401 Unauthorized` on most authenticated routes - - - - - -**JWT length validity** - -- **Default Validity**: JWT tokens are valid for the duration specified in `ttlInSeconds` during SIWE authentication -- **Minimum Duration**: 1 hour -- **Maximum Duration**: 24 hours - - -After the message is signed, you need to submit both the message and the signature to [retrieve an Access Token](/api-reference/authentication/verify-siwe-signature): -```bash cURL -curl -X POST /api/v1/auth/challenge \ - -H "Content-Type: application/json" \ - -d '{ - "message": "string", - "signature": "string", - "ttlInSeconds": 36000 - }' -``` - - - - - - -**Domain Whitelisting**: Gnosis Pay validates SIWE domains against a whitelist for security. - -- **localhost**: `localhost` (with or without ports like `localhost:3000`) is automatically allowed for local development -- **Production domains**: All production and staging domains must be registered through the [Partners Dashboard](https://partners.gnosispay.com/) during signup -- **127.0.0.1**: Avoid using `127.0.0.1` in SIWE messages as it may cause firewall blocks and "WAFForbidden" errors - -Only whitelisted domains (plus localhost) will be accepted for SIWE authentication. - - -Upon successful verification, a `jwt` is generated. -This `jwt` must be included in the Authorization header of all subsequent HTTP requests to authenticate with the Gnosis Pay API: - -```bash -Authorization: Bearer {jwt} -``` - -## Details about the Access Token - -A `jwt` is generated upon successful Sign-In with Ethereum (SIWE) verification and can remain valid for up to **24 hours**, depending on the **`ttlInSeconds`** parameter passed to SIWE, with a minimum validity period of 1 hour. - -To enhance security and prevent replay attacks, each authentication attempt requires a new and unique nonce. - -Always ensure that the `jwt` is valid before making API requests. -If an API request returns a 401 Unauthorized response due to an expired token, -your application must restart the authentication process, which requires user interaction. -This means the application must request a new nonce, prompt the user to sign the message, -and then submit the signature for verification to generate a fresh `jwt`. diff --git a/cards/assets/PSE-diagram.png b/cards/assets/PSE-diagram.png deleted file mode 100644 index 85225d0..0000000 Binary files a/cards/assets/PSE-diagram.png and /dev/null differ diff --git a/cards/assets/card-order-flow.png b/cards/assets/card-order-flow.png deleted file mode 100644 index fc21d4e..0000000 Binary files a/cards/assets/card-order-flow.png and /dev/null differ diff --git a/cards/card-order-state-transitions.mdx b/cards/card-order-state-transitions.mdx deleted file mode 100644 index 688c41d..0000000 --- a/cards/card-order-state-transitions.mdx +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Card Order State Transitions -description: "Understanding the card order state transitions" ---- -Below is the complete state transition diagram and detailed explanations. - -### State Transition Diagram - -```mermaid - stateDiagram-v2 - [*] --> PENDINGTRANSACTION : Create Order - - PENDINGTRANSACTION --> READY : Pay (free or paid) - PENDINGTRANSACTION --> TRANSACTIONCOMPLETE : Payment Complete - PENDINGTRANSACTION --> CANCELLED : Cancel - PENDINGTRANSACTION --> FAILEDTRANSACTION : Payment Failed - - TRANSACTIONCOMPLETE --> CONFIRMATIONREQUIRED : Request Confirmation - - CONFIRMATIONREQUIRED --> READY : Confirm Ready
(SOF + Phone + Address) - - READY --> CARDCREATED : Create Card
(Physical/Virtual) - - CARDCREATED --> [*] : Order Complete - CANCELLED --> [*] : Order Cancelled - FAILEDTRANSACTION --> [*] : Order Failed - - note right of PENDINGTRANSACTION - Initial state when
order is created - end note - - note right of READY - Ready for card creation
All requirements met - end note - - note right of CARDCREATED - Card successfully created
Order fulfilled - end note -``` - - - -### Card Order States - -#### Core States - -**`PENDINGTRANSACTION`** - *Initial State* -- **Description**: Order created, awaiting payment or confirmation -- **Next States**: `READY`, `TRANSACTIONCOMPLETE`, `CANCELLED`, `FAILEDTRANSACTION` -- **User Actions**: Attach transaction, confirm payment, cancel order - -**`TRANSACTIONCOMPLETE`** - *Payment Processed* -- **Description**: Payment transaction completed but requires additional verification -- **Next States**: `CONFIRMATIONREQUIRED` -- **System Actions**: Automatic transition when additional verification needed - -**`CONFIRMATIONREQUIRED`** - *Verification Needed* -- **Description**: Additional user verification required (SOF, phone, address) -- **Next States**: `READY` -- **Requirements**: Phone verified, SOF completed, address provided - -**`READY`** - *Ready for Card Creation* -- **Description**: All requirements met, ready to create physical/virtual card -- **Next States**: `CARDCREATED` -- **User Actions**: Create card with PIN (physical) or without PIN (virtual) - -#### Terminal States - -**`CARDCREATED`** - *Success* -- **Description**: Card successfully created and ready for use -- **Next States**: None (terminal state) -- **Note**: Virtual cards are immediately active; physical cards need activation - -**`CANCELLED`** - *Cancelled* -- **Description**: Order cancelled by user or system -- **Next States**: None (terminal state) -- **Note**: Only possible from `PENDINGTRANSACTION` state - -**`FAILEDTRANSACTION`** - *Payment Failed* -- **Description**: Payment processing failed -- **Next States**: None (terminal state) -- **Note**: User needs to create a new order - -### Transition Rules - - - -`PENDINGTRANSACTION` → `READY`: -- Free card: `totalAmountEUR === totalDiscountEUR` -- Paid card: Valid EURe payment to correct address -- Transaction hash validation (if required) - - - -`TRANSACTIONCOMPLETE` → `CONFIRMATIONREQUIRED`: -- Triggered when additional user verification is needed -- System determines extra checks required - - - -`CONFIRMATIONREQUIRED` → `READY`: -- User phone verified -- Source of Funds (SOF) completed -- Valid shipping address (for physical cards) - - - -`READY` → `CARDCREATED`: -- KYC approved -- Risk score: Green or Orange -- For physical cards: Encrypted PIN required -- For virtual cards: No PIN needed - - - -`PENDINGTRANSACTION` → `CANCELLED`: -- Only from `PENDINGTRANSACTION` state -- User-initiated or admin-initiated - - - -### Error Handling - -When implementing card order, consider these scenarios: - -**Invalid Transitions** -Will throw TransitionError -- Trying to cancel from READY state -- Attempting to create card from `PENDINGTRANSACTION` -- Any transition not defined in the state machine - - -**Common Error Scenarios** -- **Payment Issues**: Transaction hash already used, insufficient payment -- **User Requirements**: Missing KYC, unverified phone, missing address -- **System Issues**: Payment processor errors, card creation failures - -### Implementation Example - -```javascript -// Check current order state before taking action -const handleOrderAction = async (order, action) => { - switch (order.status) { - case 'PENDINGTRANSACTION': - if (action === 'pay') { - await confirmPayment(order.id); - } else if (action === 'cancel') { - await cancelOrder(order.id); - } - break; - - case 'READY': - if (action === 'createCard') { - await createCard(order.id, { setPin: !order.virtual }); - } - break; - - case 'CARDCREATED': - // Order complete - handle card activation if needed - break; - - default: - throw new Error(`Cannot perform ${action} on order with status ${order.status}`); - } -}; -``` - -### Cancellable States - -Orders can only be cancelled from specific states. Use the `CANCELLABLE_ORDER_STATUSES` constant: - -```javascript -const CANCELLABLE_ORDER_STATUSES = [ - 'PENDINGTRANSACTION', - 'TRANSACTIONCOMPLETE', - 'CONFIRMATIONREQUIRED', - 'FAILEDTRANSACTION' -]; -``` - - -**State Validation**: Always validate the current order state before attempting transitions. Invalid transitions will throw a `TransitionError` and return HTTP 422 status code. - diff --git a/cards/concepts.mdx b/cards/concepts.mdx deleted file mode 100644 index b8ab122..0000000 --- a/cards/concepts.mdx +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: Cards Concepts -description: "Core concepts for card management, PINs, and merchant categories" ---- - -Cards are their own domain, with lots of edge cases all around and a unique set of terms. -On this document, we aim to present the most common concepts related to operating cards -and what to expect of them. - -This is not an extensive guide and we encourage you to use this document as a way to start -your journey. Both VISA and Mastercard have great resources and guides that you can also use. - - - For transaction-related concepts like payments, refunds, and reversals, see our dedicated - [Card Transactions](/transactions) guide. - - -## Card Lifecycle - -Cards go through several states during their lifetime: - -### Card States -- **Inactive**: Card created but not yet activated -- **Active**: Card activated and ready for use -- **Frozen**: Temporarily disabled (can be unfrozen) -- **Lost**: Reported as lost (requires replacement) -- **Stolen**: Reported as stolen (requires replacement) -- **Expired**: Past expiration date (requires replacement) - -### Card Activation -New cards must be activated before first use. Activation typically involves: -- Verifying cardholder identity -- Setting initial PIN (if required) -- Confirming card receipt -- Enabling spending limits - -### Card Management Operations -- **Freeze/Unfreeze**: Temporarily disable card usage -- **Report Lost/Stolen**: Permanently disable and request replacement -- **Update Limits**: Modify spending or withdrawal limits -- **PIN Management**: Change or reset PIN - -## Disputes - -Disputes are used to recover funds for captured transactions. Their main use-case is to revert fraudulent transactions -or problems with the product or service paid for. Fraud and non-fraud disputes have different requirements and rules, -and undergo through different analysis to reach a conclusion. - -### Dispute Types -- **Fraud disputes**: Unauthorized transactions -- **Non-fraud disputes**: Issues with goods/services received -- **Processing errors**: Technical or merchant errors - -### Dispute Process -1. **Initiation**: Cardholder reports disputed transaction -2. **Investigation**: Review transaction details and evidence -3. **Provisional Credit**: Temporary refund while investigating -4. **Resolution**: Final decision and permanent credit/debit - -## Card PINs - -Visa cards have 2 places to store the PIN - the first place is on the physical card chip, called the **offline PIN**, and the second is the **online PIN** that is stored in the bank's system. While these PINs usually have the same value, there can be cases where they differ, for instance when you change the card PIN. - -When paying at a point of sale (in a restaurant or in a shop), only the offline PIN may be verified, while ATMs usually connect to the bank network and check the online PIN. When using the PSE to change the PIN, only the online PIN is changed. At this point, the offline PIN is **not updated** since physical access to the card is needed to change the PIN on the chip. To update the offline PIN, you need to go to an ATM and perform any operation. ATM transactions always go online to the card issuer. This allows the issuer to send an "issuer script" to your card, which updates the offline PIN stored on the chip, synchronizing it with any recent PIN changes made online (with PSE). Without this synchronization, your offline PIN might be outdated and cause issues for transactions that rely on offline verification. - -## MCC: Merchant Category Code - -Merchant Category Codes (MCCs) are used to classify businesses based on the types of goods or services they provide. -These codes are important as they are often used for calculating interchange fees, authorizing payments, and preventing fraud. -Additionally, specific MCCs are required for particular functionalities. diff --git a/cards/create-physical-cards.mdx b/cards/create-physical-cards.mdx deleted file mode 100644 index 6ae1be3..0000000 --- a/cards/create-physical-cards.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: "Create Physical Cards" -description: "Order physical cards for your Users." ---- - - -## Create Physical Card Process - - - - - -[This endpoint creates a new `CardOrder` with the status of `PENDINGTRANSACTION`](/api-reference/physical-card-order/create-physical-card-order): - -```bash cURL -curl -X POST /api/v1/order/create -``` - -The Card Order is created with the following data: - -- A shipping address can be specified by filling the `shippingAddress` field, see [endpoint specifications](/api-reference/physical-card-order/create-physical-card-order). If not set, the KYC address will be used for shipping. -- The total amount to be paid is set in EURe. The amount is 30.23. -- A coupon code can be applied to reduce the amount to be paid, either at signup or in the next step - - - - -[This endpoint attaches a coupon to the `CardOrder` with `orderId`](/api-reference/physical-card-order/attach-a-coupon-to-a-physical-card-order): - -```bash cURL -curl -X POST /api/v1/order/{orderId}/attach-coupon -``` - -The coupon needs to be valid, otherwise an error is thrown. -If the user has a coupon code both from the signup, and from this step, the one from the signup takes precedence. - - - - - -This is only needed if the card is not free. If `CardOrder.totalAmountEUR` is the same as the `CardOrder.totalDiscountEUR` then the card is considered free. - - -[This endpoint allows setting a transaction hash to the specified `orderId`](/api-reference/physical-card-order/register-payment-for-a-physical-card-order). - -```bash cURL -curl -X PUT /api/v1/order/{orderId}/attach-transaction \ --d '{ - "transactionHash": "0x..." -}' -``` - -The transaction can only be used once across all card orders. - - - - - -This endpoint must be called even if the card is free. It moves the card order to the `READY` status. - - -[To confirm the payment call this endpoint](/api-reference/physical-card-order/confirm-the-payment-for-a-physical-card-order): - -```bash cURL -curl -X PUT /api/v1/order/{orderId}/confirm-payment -``` - -The conditions for the payment are: - -- In order to call this endpoint, the `CardOrder.status` needs to be `PENDINGTRANSACTION`. -- If the card is not free, we check if a payment was done. - - Token used for payment needs to be EURe `0xcB444e90D8198415266c6a2724b7900fb12FC56E`. - - Payment was done to `0x3D4FD6a1A7a1382ae1d62C3DD7247254a0236847`. - - The respective EURe amount was paid in that transaction hash (partial transfers are not supported). - -If all the conditions above are met, the `CardOrder.status` is set to `READY`. - - -[Finally, you can create the Physical Card](/api-reference/physical-card-order/create-a-physical-card): - -```bash cURL -curl -X POST /api/v1/order/:orderId/create-card -``` - -When successful, this endpoint returns the `cardToken` from the newly created card. - -In order to create a `Card` out of a `CardOrder` the following conditions need to be met: - -- No cards were created out of this `orderId`. -- User needs to have a verified phone number. -- User needs to have a name set. -- User needs to be from a supported country. -- User address needs to be set. -- User needs to have an approved KYC. -- The risk score needs to be Green or Orange based on the user's answers to the Source of funds questionnaire. -- User needs to have the shipping details for the physical card order set. - - The shipping details can be different from the KYC address, as long as it is in the same country. - - Virtual cards do not require a shipping address. -- The embossed name for the card needs to be set. - - - -The card PIN should be set immediately after the card is created via the Partner Secure Elements (PSE). -Use the `cardToken` returned from the previous endpoint to set the PIN. -If a PIN is not provided upon creation, a random one will be assigned. -Changing this randomly assigned PIN requires the user to visit an ATM. Refer to the **[card PINs section](/cards#card-pins)** to learn more about the online and offline PINs. - -For detailed instructions about how to change the PIN, please refer to the [PSE SDK documentation](/cards/pse-integration). - - diff --git a/cards/create-virtual-cards.mdx b/cards/create-virtual-cards.mdx deleted file mode 100644 index a623642..0000000 --- a/cards/create-virtual-cards.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: Create Virtual Cards -description: Virtual cards work either online or tap in presence (with Apple Pay and Google Pay). ---- - -Virtual card orders have a **simplified flow** compared to physical cards, with these key differences: - -- No shipping address is required -- The card is available immediately after order creation (no shipping delay) -- Virtual cards can be used for online transactions immediately -- Thanks to Google and Apple Pay (some regions are not yet available), the card can be used for in-person transactions immediately. -- No PIN is required; the user's phone number is used as second factor authentication for some online payments -- **Cards are automatically created and activated when the order is placed** - -## How to create a Virtual Card - -[This endpoint allows you to issue a virtual card directly](/api-reference/card-management/create-a-virtual-card): - -```bash cURL -curl -X POST /api/v1/cards/virtual -``` - -Virtual cards are free and activated immediately. No shipping address or payment validation required. - - - -**Automatic Card Creation**: By calling this endpoint, the virtual card is automatically created if the user's Safe account is properly configured. You do not need to call any other endpoint. - - - -[The virtual card should appear immediately in the card list available with the endpoint](/api-reference/card-management/list-all-cards): - -```bash cURL -curl /api/v1/cards?status_code=1000 -``` - -The `status_code=1000` query parameter returns only active cards. - - -Card Limits: Users can have a **maximum of 5 active cards including physical and virtual cards combined**. Active cards exclude voided, lost, and stolen cards. - diff --git a/cards/pse-integration.mdx b/cards/pse-integration.mdx deleted file mode 100644 index 5e703f8..0000000 --- a/cards/pse-integration.mdx +++ /dev/null @@ -1,220 +0,0 @@ ---- -title: Partner Secure Elements (PSE) Integration -description: Access sensitive Card information from your client application. ---- - -## Overview - -If you want to display sensitive information, such as card numbers, in your front-end, you'll need to interact with our **Partner Secure Elements (PSE)** service. The easiest way to do this is by using the [PSE SDK](https://www.npmjs.com/package/@gnosispay/pse-sdk) from Gnosis Pay. - -To initialize the SDK, you'll need an `App ID` provided to you upon registration with Gnosis Pay. You'll also need an **ephemeral-token**. This ephemeral-token can be retrieved with a call to our private PSE API using mTLS authentication. - - - **Backend Only**: An mTLS authentication can only be performed from a - back-end. For this reason, you need a back-end responsible for retrieving the - ephemeral-token and sending it to your front-end upon request. We'll go - through each step in this guide. -{" "} - -Once your front-end has the ephemeral-token, it can initialize the PSE SDK and use it to display secure elements. - -Here's a diagram showing each step: - -```mermaid - sequenceDiagram - participant FE as Your Frontend - participant BE as Your Backend - participant API as PSE API - - FE->>BE: Get ephemeral-token - BE->>API: Get ephemeral-token (mTLS Certificates) - API-->>BE: Receive ephemeral-token - BE-->>FE: Receive ephemeral-token - - Note over FE: Initialize PSE SDK with ephemeral-token and App ID - - FE->>API: PSE SDK Requests Card Information - API-->>FE: Display Card Information Inside an Iframe -``` - -Reference implementations are available for a [front-end](https://github.com/gnosispay/ui) and a [back-end](https://github.com/gnosispay/ui/tree/main/pse-backend-demo). - ---- - -## Secure Connection Using mTLS Authentication - -**Mutual TLS (mTLS)** is a type of authentication in which two parties in a connection authenticate each other using the TLS protocol. Your back-end will establish an mTLS authentication with the Gnosis Pay private PSE API to receive an ephemeral-token. - -### How to Generate mTLS Certificates - -After signing up through the [Partners Dashboard](https://partners.gnosispay.com/), you will receive an `App ID` instantly, which will be used in the certificate generation below. You must first create a private key and then generate a Certificate Signing Request (CSR) using the `App ID` as follows: - -```graphql -# APP_ID is a string starting with `gp_` that you have received from Gnosis Pay -export APP_ID="gp_woop_123" - -# Create a private key (NEVER share with anyone) -openssl ecparam -name prime256v1 -genkey -noout -out "${APP_ID}.key.pem" - -# Create the CSR (OK to share) -openssl req -new -sha256 -key "${APP_ID}.key.pem" -out "${APP_ID}.csr.pem" -subj "/CN=${APP_ID}" -``` - -You can now share the `${APP_ID}.csr.pem` file with the Gnosis Pay team. **DO NOT EVER** share the `.key.pem` file with **ANYONE**. - -Once we receive your Certificate Signing Request, we will sign it and send you back the signed certificates. These signed certificates, along with your private key, are used to establish the connection with the PSE API. - -### How to Establish an mTLS Authentication (in Node.js) - -You should securely store the certificates in your environment along with your private key. - -Your environment should expose the certificates and private key, for example: - -```rust -SIGNED_CERTIFICATES="-----BEGIN CERTIFICATE----- -ABCQz .... ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -DEFC7 .... ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -GHICc .... ------END CERTIFICATE-----" - -PRIVATE_KEY="-----BEGIN EC PRIVATE KEY----- -ABCD.... ------END EC PRIVATE KEY-----" -``` - -Here's a Node.js implementation to request the ephemeral-token in two different ways: - - - -```js Using Axios -const httpsAgent = new https.Agent({ - cert: process.env.SIGNED_CERTIFICATES, - key: process.env.PRIVATE_KEY, - rejectUnauthorized: true, // Ensure SSL verification -}); - -const ephemeralTokenRequest = await axios({ - httpsAgent: httpsAgent, - method: "POST", - url: `https://api-pse.gnosispay.com/api/v1/ephemeral-token`, - headers: { "Content-Type": "application/json" }, - // Axios adds the user-agent automatically -}); -``` - -```js Using https.request -import https from "https"; - -const httpsAgent = new https.Agent({ - cert: CERT, - key: KEY, - rejectUnauthorized: true, -}); - -const req = https.request( - { - hostname: "api-pse.gnosispay.com", - path: "/api/v1/ephemeral-token", - method: "POST", - headers: { - "Content-Type": "application/json", - "User-Agent": "User-Client/1.0.0", - }, - agent: httpsAgent, - }, - (res) => { - let data = ""; - res.on("data", (chunk) => { - data += chunk; - }); - - res.on("end", () => { - console.log("Status:", res.statusCode); - - try { - const parsedData = JSON.parse(data); - console.log("Response:", parsedData); - } catch { - console.log("Raw response:", data); - } - }); - } -); - -req.on("error", (error) => { - console.error("Request error:", error); -}); -req.end(); -``` - - - -Please refer to our [API documentation](/api-reference/endpoints/generate-a-new-ephemeral-token) on the PSE service for more information about the specific endpoint. - - - The ephemeral-token, as its name suggests, is valid for a very short time - frame. It is advised to generate a new one for every usage of the SDK. - - ---- - -## How to Use the PSE SDK - -The PSE SDK will help you display sensitive information. It injects secure iframes into your front-end and exposes callbacks to interact with it. Refer to the [PSE SDK documentation](https://www.npmjs.com/package/@gnosispay/pse-sdk) for instructions and examples. - -## How to Customize the Style of Secure Elements in the iframe - -For security reasons, the only way to apply custom styling to iframe elements is to prepare and share a **CSS file** with the Gnosis Pay team. This file, named `.css`, will be incorporated into the iframe. - -Standard styling is applied to the iframe elements by default. You can override the style of these classes and IDs as needed. Here are some of them: - -#### Card Data - -- `.pse-container` - A shared class for all iframe containers. -- `#pse-card-data-container` - The main container for displaying card data. -- `.pse-card-field` - The container for each card data field (card number, expiry date, security code). -- `.pse-card-label` - Labels for each field. -- `.pse-card-value` - The container for the actual card data values. - -#### Set PIN Form - -- `.pse-container` - A shared class for all iframe containers. -- `#pse-set-pin-form` - The main form container. -- `#pse-set-pin-success-container` - The success state container. -- `.pse-set-pin-container` - The base container class. -- `.pse-set-pin-button` - The base class for all buttons. -- `#pse-continue-pin-button` - The continue button. -- `#pse-done-button` - The done button. - ---- - -### Styling Guide - -Here is a suggested workflow to customize the styling: - -1. In your front-end, load the element you wish to customize (e.g., the card data). -1. Locate the custom CSS file with your name in either the "**Style Editor**" in Firefox or the "**Sources**" panel on Chrome/Brave. In the example below, the file is `gnosis_pay_ui.css`. -1. Apply your desired styling. The changes will be reflected in your interface immediately. -1. Save the file and send it to Gnosis Pay for application in production. - -Here is an example of overriding the `.pse-card-field` class in Firefox: -![custom css](./assets/css-styling.png) - -## React Native Integration - -If you'd like to integrate the PSE SDK into a React Native application, you can use the [React Native PSE SDK](https://www.npmjs.com/package/@gnosispay/pse-react-native) package to help integrating the WebView piece. - -However, as the PSE requires hosting on your domain, you will still need to host the PSE Frame on your backend, on your domain. - -This frame can be as simple as an HTML page that invokes the PSE JS SDK. - -In our reference implementation repository, next to the examples of implementing backend endpoint for ephemeral token generation, you can -find [an example of such page](https://github.com/gnosispay/ui/blob/main/pse-backend-demo/src/static/native-webview.html) returned by the same backend that generates the ephemeral token. - -Note that this page should be responsible for fetching the ephemeral token from your backend, and passing it to the PSE JS SDK. - -You will then pass this URL to the React Native WebView component, through the `webViewUrl` prop, together with other parameters that you can find in the [React Native PSE SDK documentation](https://www.npmjs.com/package/@gnosispay/pse-react-native). diff --git a/changelog/index.mdx b/changelog/index.mdx new file mode 100644 index 0000000..8310fa6 --- /dev/null +++ b/changelog/index.mdx @@ -0,0 +1,9 @@ +--- +title: "Changelog" +description: "New features, improvements, and fixes for Gnosis Pay" +--- + + + We're putting the finishing touches on our public API and docs. Changelog + entries will start appearing here shortly. + diff --git a/docs.json b/docs.json index 607aaff..4b7f311 100644 --- a/docs.json +++ b/docs.json @@ -1,106 +1,50 @@ { + "$schema": "https://mintlify.com/docs.json", "theme": "mint", - "name": "Gnosis Pay Documentation", + "name": "Gnosis Pay", "colors": { - "primary": "#707A2D", - "light": "#D8EB81", - "dark": "#919E3A" + "primary": "#766DE5", + "light": "#9994ED", + "dark": "#1A1751" + }, + "appearance": { + "default": "light", + "strict": false + }, + "logo": { + "light": "/static/img/logo-dark.svg", + "dark": "/static/img/logo-light.svg" + }, + "navbar": { + "links": [ + { + "label": "Contact Support", + "href": "mailto:support@example.com" + } + ], + "primary": { + "type": "button", + "label": "Sign up as Partner", + "href": "https://dashboard.example.com/signup" + } }, - "favicon": "/static/img/favicon.ico", "navigation": { "tabs": [ { "tab": "Guides", "groups": [ { - "group": "Introduction to Gnosis Pay", - "icon": "play", - "pages": ["index", "integration-model", "auth", "onboarding-flow"] - }, - { - "group": "Accounts", - "icon": "user", - "pages": [ - "account", - "account/update-authenticated-account", - "account/adding-safe-owner" - ] - }, - { - "group": "On/Off Ramps", - "icon": "landmark", - "pages": [ - "on-off-ramps/index", - "on-off-ramps/iban-integration", - "on-off-ramps/kyc-sharing" - ] - }, - { - "group": "Cards", - "icon": "credit-card", - "pages": [ - "cards/concepts", - "cards/create-physical-cards", - "cards/create-virtual-cards", - "cards/card-order-state-transitions", - "cards/pse-integration" - ] - }, - { - "group": "Card Transactions", - "icon": "receipt", - "pages": [ - "transactions/index", - "transactions/lifecycle", - "transactions/integration-guide" - ] - }, - { - "group": "On-chain", - "icon": "cubes", - "pages": [ - "gp-onchain/overview", - "gp-onchain/about-GP-safe", - "gp-onchain/sign-message", - "gp-onchain/daily-limit", - "gp-onchain/withdraw-funds-from-safe", - "gp-onchain/third-party-bridges" - ] - }, - { - "group": "Webhooks", - "icon": "webhook", - "pages": [ - "webhooks/introduction", - "webhooks/getting-started", - "webhooks/events" - ] + "group": "Get Started", + "pages": ["intro", "whybuild", "architecture"] } ] }, { - "tab": "API reference", - "groups": [ - { - "group": "Authentication", - "pages": ["api-reference/intro", "api-reference/auth-app"] - }, - { - "group": "API reference", - "openapi": "https://api.gnosispay.com/api-docs/spec.json" - }, - { - "group": "PSE API", - "openapi": "https://api-pse-public.gnosispay.com/api-docs/spec.json" - } - ] + "tab": "Changelog", + "pages": ["changelog/index"] } ] }, - "logo": { - "light": "/static/img/Logo-black.svg", - "dark": "/static/img/Logo-white.svg" - }, "contextual": { "options": [ "copy", diff --git a/gp-onchain/about-GP-safe.mdx b/gp-onchain/about-GP-safe.mdx deleted file mode 100644 index de4ffb7..0000000 --- a/gp-onchain/about-GP-safe.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: About GP Safe -description: Gnosis Pay's on-chain infrastructure is built on top of the [Safe protocol](https://safe.global), providing a secure and programmable foundation for digital payments. Each user gets a unique Safe account that acts as their on-chain wallet, with additional modules that enable spending limits, time delays, and delegated spending capabilities. ---- - -The Gnosis Pay Safe consists of a core Safe account with three main modules attached: - -1. **Core Safe Account**: The base Safe that holds funds and manages ownership -2. **Delay Module**: Controls transaction timing and provides security delays -3. **Roles Module**: Manages spending permissions and allowance limits -4. **Bouncer Module**: Handles account access validation and security - -### Gnosis Pay Safe Setup Process - -To understand how a Gnosis Pay Safe differs from a standard Safe, let's examine the [account setup flow](https://github.com/gnosispay/account-kit/blob/main/src/entrypoints/accounts-actions/accountSetup.ts#L88) that transforms a basic 1/1 Safe into a fully configured Gnosis Pay Safe Account: - - - -- **Ownership Transfer**: Swaps the Safe owner to an inaccessible address (`0x0000000000000000000000000000000000000002`) -- **Module Enablement**: Enables both the Roles and Delay modules on the Safe - - - -- **Deployment**: Creates a new Delay module instance for the account -- **Cooldown Configuration**: Sets the transaction delay period (typically 3 minutes) -- **Expiration Setting**: Configures how long queued transactions remain valid -- **Owner Access**: Grants the initial Safe owner (the owner before the ownership transfer) access to the delay module - - - -- **Deployment**: Creates a new Roles module instance for the account -- **Allowance Configuration**: Sets up spending limits with: - - Initial balance amount - - Maximum balance cap - - Refill amount per period - - Time period for refills -- **Role Assignment**: Grants spending permissions to designated spender addresses -- **Target Scoping**: Restricts spending to specific token contracts -- **Function Scoping**: Limits operations to ERC20 transfer functions only -- **Ownership Transfer**: Transfers control to the Bouncer module - - - -- **Deployment**: Creates the final security layer -- **Access Control**: Manages who can interact with the account - - - -### Spending Control Flow - -The spending process follows this pattern: - -1. **Spender Request**: A designated spender requests a transaction -2. **Allowance Check**: The Roles module verifies spending limits -3. **Delay Enforcement**: If limits allow, the transaction enters the delay queue -4. **Time Delay**: The transaction waits for the configured cooldown period -5. **Execution**: After the delay, the transaction can be executed -6. **Balance Update**: The allowance balance is updated accordingly diff --git a/gp-onchain/daily-limit.mdx b/gp-onchain/daily-limit.mdx deleted file mode 100644 index 6dc9ebf..0000000 --- a/gp-onchain/daily-limit.mdx +++ /dev/null @@ -1,174 +0,0 @@ ---- -title: "Change GP Safe daily spending limit" -description: "Manage daily spending limits for Gnosis Pay Safe accounts" ---- - -The daily spending limit feature allows partners to manage daily spending limits for Gnosis Pay Safe accounts. - -All transactions are gasless, enabling users to perform these operations completely free of charge. - -## Overview - -The daily spending limit update process involves the following steps: - -1. **Fetch EIP-712 Typed Data** - Get the EIP-712 typed data for wallet signing -2. **Sign and Submit** - Sign the EIP-712 data and submit the update -3. **Monitor Update** - Wait for the delay relay to process the change and check the new limit - - -- The signing wallet must be one of the Gnosis Pay Safe signers (EOA or smart contract wallet) -- For smart contract wallets, signatures are verified using ERC-1271 standard -- Updates are processed after a 3-minute delay as transactions are going through the Gnosis Pay delay relay - - -## Daily Limit Update Process - - - - -[Get the EIP-712 typed data that needs to be signed by the user's wallet](/api-reference/account-management/get-eip-712-typed-data-for-setting-daily-limit): - -```bash cURL -curl -X GET \ - /api/v1/accounts/daily-limit/transaction-data?newLimit=${newLimit} -``` - -The response contains EIP-712 typed data with `domain`, `types`, `primaryType`, and `message` fields that need to be signed by the user's wallet using the EIP-712 standard. - - - - -**Safe Activation Required**: This step only works when the Gnosis Pay Safe account has been fully activated and its modules are deployed. If you receive an error during submission, ensure that the Safe account activation process has been completed first. - -For more information about Safe account setup and module deployment, see the [Safe Management API documentation](/api-reference/safe-management/deploy-and-setup-a-safe). - - -The EIP-712 typed data from Step 1 must be signed by the user's wallet using the EIP-712 signature standard. - - -**Wallet Requirements:** -- The wallet must be a signer of the Gnosis Pay Safe account -- For **EOA wallets**: Standard EIP-712 signature is used -- For **smart contract wallets**: ERC-1271 signature verification is used, and you must include the `smartWalletAddress` field in the request body - - -Once signed, [submit the transaction to update the daily limit](/api-reference/account-management/set-new-daily-spending-limit): - - -```bash cURL -# For EOA wallets -curl -X PUT /api/v1/accounts/daily-limit \ - -d '{ - "newLimit": 1500, - "signature": "0x...", - "message": { - "salt": "0x...", - "data": "0x..." - } -}' - -# For smart contract wallets (include smartWalletAddress) -curl -X PUT /api/v1/accounts/daily-limit \ - -d '{ - "newLimit": 1500, - "signature": "0x...", - "message": { - "salt": "0x...", - "data": "0x..." - }, - "smartWalletAddress": "0x..." -}' -``` - - - - -The daily limit change is processed through a delay relay mechanism that executes after 3 minutes. - -You can monitor the transaction status using the [delay-relay monitoring endpoint](/api-reference/safe-management/list-delayed-transactions) -or by checking your Safe's transaction history. - -Additionally, you can also [poll the following endpoint to check when the new limit becomes active](/api-reference/account-management/get-current-daily-spending-limit): - - -```bash cURL -curl -X GET /api/v1/accounts/daily-limit -``` - - - - -## Complete Implementation Example - -The following example demonstrates the complete flow: - -```typescript - -const newDailyLimit = 1337; - -/** - * Step 1: Fetch EIP-712 typed data for signing - */ -const response = await fetch( - `https://api.gnosispay.com/api/v1/accounts/daily-limit/transaction-data?newLimit=${newDailyLimit}` -); -const { data: typedData } = await response.json(); - -/** - * Step 2: Sign the EIP-712 typed data - */ -const signature = await walletClient.signTypedData({ - ...typedData, - domain: { - ...typedData.domain, - verifyingContract: typedData.domain.verifyingContract as `0x${string}`, - }, -}); - -/** - * Step 3: Submit the signed data - */ -const submitResponse = await fetch("https://api.gnosispay.com/api/v1/accounts/daily-limit", { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - newLimit: newDailyLimit, - signature, - message: typedData.message, - // Include smartWalletAddress if using a smart contract wallet - // smartWalletAddress: "0x...", - }), -}); - -const { data: updateResult } = await submitResponse.json(); - -/** - * Step 4: Monitor for the updated limit - */ -console.log(`Daily limit update submitted with ID: ${updateResult.id}`); -console.log(`Status: ${updateResult.status}`); -console.log("Limit change will be processed after the 3-minute delay period"); - -// Optional: Poll for the updated limit -const pollForUpdate = async () => { - const checkResponse = await fetch( - "https://api.gnosispay.com/api/v1/accounts/daily-limit" - ); - const { data: { dailyLimit: updatedLimit } } = await checkResponse.json(); - - if (updatedLimit === newDailyLimit) { - console.log(`Limit successfully updated to: ${updatedLimit}`); - return true; - } - - console.log(`Waiting for update... Current: ${updatedLimit}, Target: ${newDailyLimit}`); - return false; -}; - -// Poll every 30 seconds until the update is complete -const interval = setInterval(async () => { - if (await pollForUpdate()) { - clearInterval(interval); - } -}, 30000); -``` diff --git a/gp-onchain/overview.mdx b/gp-onchain/overview.mdx deleted file mode 100644 index 978fb0c..0000000 --- a/gp-onchain/overview.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Overview -description: Understanding the on-chain integration options for Gnosis Pay, including the Account Kit SDK and direct contract integration approaches. ---- - -## Account Kit - -We provide the Account Kit as a way to accelerate on-chain integration for the TypeScript/JavaScript ecosystem. This SDK abstracts away the complexity of our multi-module Safe architecture, making it easier for developers to integrate with Gnosis Pay's on-chain infrastructure directly. - -Installing instructions and comprehensive examples are available in the [Account Kit repository](https://github.com/gnosispay/account-kit). The SDK is open source and actively maintained by the Gnosis Pay team. - -All examples in this section of the documentation assume you are using the Account Kit SDK. - -## Direct Integration without Account Kit - -For other programming languages, you can use their own cryptocurrency libraries (e.g., go-ethereum for Go, web3.py for Python, and so on) and integrate directly with the contracts. However, this approach requires additional steps since the Account Kit relies on TypeScript/JavaScript projects from Gnosis such as `@gnosis.pm/safe-contracts`, `@gnosis.pm/zodiac`, and related dependencies. - -Integrating directly means you'll need to explore the Account Kit source code and its dependencies to understand: -- **Contract Function Calls**: Which specific functions are being called on the Safe, Delay, Roles, and Bouncer modules -- **Payload Encoding**: How transaction data is encoded and structured for each operation -- **Module Interactions**: The sequence and coordination between different modules -- **Parameter Validation**: What parameters are required and how they're validated - -This reverse-engineering approach is entirely doable for teams with strong smart contract integration experience, but it requires significant additional development time compared to using the Account Kit directly. \ No newline at end of file diff --git a/gp-onchain/sign-message.mdx b/gp-onchain/sign-message.mdx deleted file mode 100644 index f88d8a0..0000000 --- a/gp-onchain/sign-message.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Sign Messages with GP Safe -description: Learn how to sign messages using your GP Safe account with the delay module for enhanced security. ---- - -Unlike traditional EOA (Externally Owned Account) message signing, GP Safe message signing goes through the delay module. - - -Message signing with GP Safe requires the account to be fully configured with the delay module enabled. If you haven't set up your Safe account yet, see the [Safe Account Configuration](/onboarding-flow#4-safe-account-configuration) section first. - - -## Message Signing Process - -GP Safe message signing follows the same two-phase pattern as other delayed operations: - -1. **Enqueue Phase**: Submit the message signing request to the delay queue -2. **Dispatch Phase**: Execute the message signing after the delay period expires - -[You can find a complete working example here.](https://github.com/gnosispay/account-kit/blob/main/examples/src/sign-safe-message/index.ts) - - - -First, prepare the message you want to sign. - -This can be any string, such as: - -```typescript -const message = "Hello, this is a message to be signed by my GP Safe!"; -``` - -Or structured data that needs to be signed by your GP Safe account. - - - - -Use the `populateSignMessageEnqueue` function to create a transaction that submits your message signing request to the delay queue. - -```typescript -import { populateSignMessageEnqueue } from "@gnosispay/account-kit"; - -const owner: Signer = {}; // Your wallet signer -const account = "0x..."; // Your GP Safe address -const chainId = 100; // Gnosis Chain - -const enqueueTx = await populateSignMessageEnqueue( - { account, chainId }, - message, - // EIP-712 signature callback - ({ domain, primaryType, types, message }) => - owner.signTypedData(domain, primaryType, types, message) -); - -// Send the enqueueTx using your wallet -``` - - -This transaction will be queued in the delay module and cannot be executed immediately. The delay period (typically 3 minutes) must pass before the message can be signed. - - - - -The message signing request is now in the delay queue. You must wait for the configured cooldown period to expire before proceeding. - - -The default delay period is 3 minutes (180 seconds). This provides time for you to review and potentially cancel the operation if needed. - - -You can check the delay queue status using the `accountQuery` function from `@gnosispay/account-kit`. - - - - -Once the delay period has expired, use the `populateSignMessageDispatch` function to execute the queued message signing operation. - -```typescript -import { populateSignMessageDispatch } from "@gnosispay/account-kit"; - -const dispatchTx = populateSignMessageDispatch( - { account }, - message -); - -// Send the dispatchTx using your wallet -``` - - -Unlike the enqueue transaction, the dispatch transaction doesn't require a signature since it's executing a previously authorized operation. - - - - - diff --git a/gp-onchain/third-party-bridges.mdx b/gp-onchain/third-party-bridges.mdx deleted file mode 100644 index 4b6f45e..0000000 --- a/gp-onchain/third-party-bridges.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: "Third-Party Bridges" ---- -Gnosis Pay partners can select and integrate bridge aggregators based on their specific infrastructure requirements and target chain support. The following recommended solutions ensure optimal cross-chain functionality for end users. - - -### Recommended Bridge Aggregators - - - - - Intent-based bridging with deep liquidity for Gnosis Chain, supporting EURe and GBPe. Converts Bitcoin and non-EVM assets to Gnosis Chain in a single transaction. - - - - Cross-chain bridge aggregator offering best-price, auto-routed swaps across multiple blockchains. - - - - Cross-chain liquidity aggregation protocol enabling seamless token transfers across multiple chains and bridges. - - - - Native Gnosis Chain protocol offering MEV-protected, gasless bridging from Ethereum. - - - - Handles complex cross-chain routes like Solana USDC → Gnosis with custom message passing. - - - diff --git a/gp-onchain/withdraw-funds-from-safe.mdx b/gp-onchain/withdraw-funds-from-safe.mdx deleted file mode 100644 index e14b640..0000000 --- a/gp-onchain/withdraw-funds-from-safe.mdx +++ /dev/null @@ -1,162 +0,0 @@ ---- -title: "Withdraw funds from GP Safe" -description: "How to send funds from the Gnosis Pay Safe to your EOA or somewhere else." ---- - -The withdrawal feature allows partners to withdraw tokens from Gnosis Pay Safe accounts to any external address **on the Gnosis Chain**. -All transactions are gasless, enabling users to perform these operations completely free of charge. - -The withdrawal process involves the following steps: - -1. **Fetch Transaction Data** - Get the EIP-712 typed data for signing -2. **Sign and Submit** - Sign the transaction and submit the withdrawal -3. **Monitor Execution** - Wait for the delay relay to process the withdrawal - - -Please remember the following: -- The signing wallet must be one of the Gnosis Pay Safe signers (EOA or smart contract wallet) -- For smart contract wallets, signatures are verified using ERC-1271 standard -- Withdrawals are processed after a 3-minute delay as transactions are going through the Gnosis Pay delay relay -- Cards are temporarily frozen for 3 minutes during withdrawal processing as a security measure - - -## Withdrawal Process - - - - -Get the EIP-712 typed data that needs to be signed by the user's wallet [(spec)](/api-reference/account-management/retrieve-transaction-data-for-withdrawing-from-safe): - -```bash cURL -curl -X GET \ -/api/v1/accounts/withdraw/transaction-data?tokenAddress=${tokenAddress}&to=${toAddress}&amount=${amount} -``` - -The response contains EIP-712 typed data that needs to be signed by the user's wallet. - - - -The EIP-712 typed data from Step 1 must be signed by the user's wallet using the EIP-712 signature standard. - - -**Wallet Requirements:** -- The wallet must be a signer of the Gnosis Pay Safe account -- For **EOA wallets**: Standard EIP-712 signature is used -- For **smart contract wallets**: ERC-1271 signature verification is used, and you must include the `smartWalletAddress` field in the request body - - - - -Once signed, [submit the transaction to execute the withdrawal](/api-reference/account-management/withdraw-from-safe-with-signature): - -```bash cURL -# For EOA wallets -curl -X POST \ -/api/v1/accounts/withdraw \ --H "Content-Type: application/json" \ --d '{ - "tokenAddress": "0x123456....", - "to": "0x78910...", - "amount": "1000000000000000000", - "signature": "0x1234567890abcdef...", - "message": { - "salt": "0x1234567890...abcdef", - "data": "0x123456...00de0b6b3a7640000" - } -}' - -# For smart contract wallets (include smartWalletAddress) -curl -X POST \ -/api/v1/accounts/withdraw \ --H "Content-Type: application/json" \ --d '{ - "tokenAddress": "0x123456....", - "to": "0x78910...", - "amount": "1000000000000000000", - "signature": "0x1234567890abcdef...", - "message": { - "salt": "0x1234567890...abcdef", - "data": "0x123456...00de0b6b3a7640000" - }, - "smartWalletAddress": "0x..." -}' -``` - - - - -The withdrawal is processed through a **delay relay mechanism** that executes after 3 minutes. - -You can monitor the transaction status using the [delay-relay monitoring endpoints](/api-reference/safe-management/list-delayed-transactions) or by checking your Safe's transaction history. - - - - - - -## Complete Implementation Example - -The following example demonstrates the complete flow: - -```typescript -const tokenAddress = "0x..."; // Token contract address -const toAddress = "0x..."; // Destination address -const amount = "1000000000000000000"; // Amount in token base units - -/** - * Step 1: Fetch transaction data for signing - */ -const response = await fetch( - `https://api.gnosispay.com/api/v1/accounts/withdraw/transaction-data?tokenAddress=${tokenAddress}&to=${toAddress}&amount=${amount}` -); -const { data: typedData } = await response.json(); - -/** - * Step 2: Sign and submit the transaction - */ -const signature = await walletClient.signTypedData({ - ...typedData, - domain: { - ...typedData.domain, - verifyingContract: typedData.domain.verifyingContract as `0x${string}`, - }, -}); - -const submitResponse = await fetch("https://api.gnosispay.com/api/v1/accounts/withdraw", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - tokenAddress, - to: toAddress, - amount, - signature, - message: typedData.message, - // Include smartWalletAddress if using a smart contract wallet - // smartWalletAddress: "0x...", - }), -}); - -const { data: transactionResult } = await submitResponse.json(); - -/** - * Step 3: Monitor execution (optional) - */ -console.log(`Withdrawal submitted with ID: ${transactionResult.id}`); -console.log(`Status: ${transactionResult.status}`); -console.log("Transaction will be processed after the 3-minute delay period"); -``` - - -## Security Considerations - -- Make sure the user is aware this transfer is on the Gnosis Chain -- Remember that transactions cannot be reversed once executed -- Cards are temporarily frozen during the withdrawal process as a security measure - -## Token Support - -This feature supports withdrawing any ERC-20 token that exists in the Safe account. Make sure to: - -- Use the correct token contract address -- Specify the amount in the token's base units (considering decimals) -- Ensure sufficient token balance in the Safe account diff --git a/index.mdx b/index.mdx index 723fa91..06281e2 100644 --- a/index.mdx +++ b/index.mdx @@ -1,71 +1,85 @@ --- -title: "Introduction" -description: "Welcome to GnosisPay Documentation" +title: "Gnosis Pay" +description: "White-label infrastructure for instant card issuance, fiat rails, and multi-currency accounts" +mode: "custom" --- -This documentation is designed to help developers and businesses leverage our APIs -to enable seamless card issuance, management, and user experiences for their customers. +
-With Gnosis Pay, you can offer your users a simple way to interact with crypto payments in the real world while enjoying -the security of self-custodial wallets. By following the steps in this guide, you can ship stablecoin program for users and allow them to spend crypto. +
+ Gnosis Pay +
- - Get a Gnosis Pay account today by signing in to the Gnosis Pay webapp and start spending crypto in the real world. - +
+

+ Ship stablecoin
card programs
in minutes +

+

+ White-label infrastructure for instant card issuance, fiat rails, and multi-currency accounts. +

+ + Start integrating today + +
-## Gnosis Pay Functionality +
-Gnosis Pay's first product is Gnosis Card: a stablecoin based visa debit card, -enabling users to spend their digital assets in the traditional economy using the Gnosis Pay network. - - - Easily spend crypto like you spend cash. - - - > - Accepted at 80+ million Visa merchants worldwide. + + + Deploy Gnosis Pay infra across different blockchains. Live on Celo and Gnosis Chain. - - - Take control of your money through your SAFE account. You own your keys, you own your crypto. + + Enable your ecosystem's stablecoin as a spending currency, fully on-chain with native support. + + + Accept KYC already verified by trusted partners, no need of double KYC verification process. + + + Reversals are processed instantly as the message is received by network. + + + EUR, USD, and GBP accounts available by default, with full multi-currency support. + + Live across Brazil, the EU, UK, Cyprus, Colombia, the Philippines, Mexico, Japan, Indonesia, Thailand, and Singapore and expanding. + + - - Add savings and investment features by connecting to DeFi protocols through plug-and-play integrations. - + - - Earn up to 5% cashback on eligible transactions based on the amount of GNO you hold in your wallet. - +
- - On and Off ramp integrations are available with our integrated ramp partners such as Noah, Monerium and Avenia. - - + +
diff --git a/integration-model.mdx b/integration-model.mdx deleted file mode 100644 index ff586b0..0000000 --- a/integration-model.mdx +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: "Integration models : Permissionless vs Partnerships" -description: "Gnosis Pay offers two ways of integrating Gnosis Pay APIs. Whether you’re building as a developer or looking to launch a whitelisted branded solution, Gnosis Pay is designed to be flexible and accessible for all developers and businesses." ---- -## Integration Models: Permissionless vs Partnerships - -Gnosis Pay offers two ways to get started: - -1. Permissionless Integration -2. Partnership Integration - ---- -## 1. Permissionless Integration (Free Plan Only) - -Developers can start building with our APIs right away—no need to contact us or go through an approval process. This lets you quickly enable crypto payments, card issuance, and account features for your users. - -Permissionless API Flow - Start building and testing with Gnosis Pay APIs - - -This integration requires no special credentials. Simply authenticate with our APIs using SIWE (Sign-In with Ethereum) to receive a JWT token and start building. - - -**Who it's for:** Developers or small teams who want to explore the APIs, prototype, or run very lightweight programs. - - - - - ✅ Make API calls immediately — no API keys, approvals, or contracts - - ✅ Issue and manage Gnosis-branded cards for testing and early users - - ✅ Experiment with basic functionality (card issuance, transactions, balance queries) - - - - - ❌ Attribute card activity back to your business (no webhooks, no tracking) - - ❌ Display sensitive card details (requires Partner Secure Element) - - ❌ Launch a branded card program at scale - - - - -Once you want to connect card activity to your app/business or expose sensitive card details, you'll need to switch into Partnership mode. - - ---- - -## 2. Partnership Integration (Free, Startup, and Enterprise) - -If your company is interested in deeper collaboration—such as branded cards, custom user experiences, fee rebates, or co-marketing opportunities — you can sign up through our [Partners Dashboard](https://partners.gnosispay.com/). - - -**Domain Whitelisting Required**: During partner signup, you must provide all production and staging domains where your application will be hosted. These domains are whitelisted for SIWE (Sign-In with Ethereum) authentication and CORS. - -- **localhost**: Automatically allowed for local development (e.g., `localhost:3000`) -- **Production/Staging**: All non-localhost domains must be registered during signup -- Applications running on non-whitelisted domains will face authentication failures - - - - -Partnership API Flow - Start building and testing with Gnosis Pay APIs - -**Who it's for:** Any partner ready to move beyond experimentation and operate a live program. - - - - - **Partner ID** → Required for webhooks, attribution, and tracking - - - **App ID** → Required for Partner Secure Element (PSE) access - - - - - Branded cards and custom experiences - - Multi-market support - - BIN sponsorship - - Compliance oversight - - - - - - Direct access to Gnosis Pay team for technical guidance and business support. - - - - Complete integration into your systems with webhooks and analytics. - - - -**Mandatory for:** Startup and Enterprise plans, but also available to Free Plan partners who want to extend beyond pure experimentation. - - -## **Getting Your PartnerID and APP_ID** - -Through our self-service [Partners Dashboard](https://partners.gnosispay.com/), partners can: -- **PartnerID**: A unique identifier for partner organization within the Gnosis Pay ecosystem, enabling us to track user acquisition and attribute activity to integration. - -- **APP_ID**: An application-specific identifier that allows partners to authenticate with the PSE (Partner Secure Elements) and establish secure mTLS connection authentication between partner and Gnosis Pay. - Partners Dashboard - Self-service partner registration and management - - To get your PartnerID and APP_ID through the Partners Dashboard: - 1. Sign up at [partners.gnosispay.com](https://partners.gnosispay.com/) - 2. Include your company details and app domains (required for CORS whitelisting) - 3. Get your PartnerID and APP_ID instantly upon registration - 4. Handle PSE integration setup, CSR signature, and webhook configuration directly through the dashboard - - **Critical**: You must include all your production and staging domains during signup. While localhost is automatically allowed for development, all other domains must be whitelisted for both CORS and SIWE authentication. Missing domains will cause authentication failures and API access issues. - diff --git a/intro.mdx b/intro.mdx new file mode 100644 index 0000000..6ca8640 --- /dev/null +++ b/intro.mdx @@ -0,0 +1,42 @@ +--- +title: "Introduction to Gnosis Pay" +description: "Stablecoin card-as-a-service infrastructure for wallets, neobanks, exchanges, and payment processors" +--- + +Gnosis Pay is a B2B stablecoin card program manager that enables fintech products like neobanks, wallets, etc to launch cards to users. Gnosis Pay Partners can run their own branded card programs and own their user relationships, while our platform handles the complexity of card network integrations, settlement flows, compliance, and payment processing. + +Partners can go further and customize their integration to fully own the end-to-end experience bringing their own KYC and user onboarding flows, or using the Gnosis Pay APIs directly to onboard users and issue cards. + +Gnosis Pay APIs are designed with developer experience who are integrating this in product. Developement team gets access to a dedicated sandbox environment, where they can test Visa transactions through the fully integrated Pismo Simulator, and run KYC flows. + + +This documentation covers the guides for the product, alongside the full API reference. + + +## Built for teams turning crypto into everyday finance + + + + Turn DeFi into a daily financial hub with branded cards, auto-yield deposits, and real-world payments. + + + Expand into new markets with one integration, offering local payment rails like SEPA, Pix, and Faster Payments — without needing local banking infrastructure. + + + With one API, exchanges enable users to spend USDC or EURC directly from their accounts via instant virtual cards that support retention and enhance utility. + + + Launch white-labeled card issuing with ease using your existing KYC/AML flow, in a fully branded experience. + + + +## What's next + + + + Learn the core concepts and building blocks behind Gnosis Pay. + + + Explore endpoints to integrate cards, KYC, and payments. + + diff --git a/on-off-ramps/iban-integration.mdx b/on-off-ramps/iban-integration.mdx deleted file mode 100644 index 79a1ecf..0000000 --- a/on-off-ramps/iban-integration.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: IBAN Integration -description: IBAN cash-in to the Gnosis Pay Account via Monerium ---- - -We offer IBAN cash-in to the Gnosis Pay Account via Monerium for users in Europe (EU) and Switzerland. - -Monerium's IBAN integration enables direct connection between the owner of the GP Safe (which is the authenticated account address for SIWE) and Monerium. - -- By calling the integrations endpoint (see below), it shares the user's KYC with Monerium. - -- You can use [Monerium API](https://monerium.dev/api-docs/v2) to authenticate with SIWE and perform actions enabled by the Monerium API - -- This API operates independently of the GP Safe and does not involve signatures on the delay module. - - - -## Check Availability - -[First you need to check if the user is eligible to have an IBAN](/api-reference/iban/check-iban-availability): - -```bash cURL -curl -X GET /api/v1/ibans/available -``` - -If you receive a `"available": false`, the flow ends here. - - -Requirements that the Gnosis Pay user must have: -- Valid date of birth -- An active EURe Safe Account -- KYC verified and approved -- Residency in a supported country -- First and last name defined (or name) -- Valid location fields filled in (address1, postalCode, city and country, none of these fields can be missing) -- Nationality from a supported country ([see allowed and restricted nationalities](https://help.gnosispay.com/hc/en-us/articles/39558629298708-Eligible-Nationalities-for-the-Gnosis-Pay-IBAN-Feature)) - - -## Enabling the IBAN Integration - - - - - -Monerium requires a specific message to be signed by the user's wallet to prove the ownership of the wallet. -[Use this endpoint to get the exact message that needs to be signed](/api-reference/iban/get-the-message-that-needs-to-be-signed-for-iban-activation): - -```bash cURL -curl -X GET /api/v1/ibans/signing-message -``` - - -We cannot request new IBANs nor transfer existing IBANs until the message signature is completed. This signature is a mandatory requirement from Monerium to verify ownership of the address. - - - - - - -Use the message string returned from [step 1](#signing-the-monerium-message) to generate a signature with the user's wallet. - -To request signature from the user's wallet, you can follow this [demo signature implementation](#message-signing-example-with-viem). - - - - - -[Make a POST request to this endpoint to request IBAN integration for Gnosis Pay user](https://docs.gnosispay.com/api-reference/iban/create-a-new-monerium-integration): - -```bash cURL -curl --request POST \ - --url https://api.gnosispay.com/api/v1/integrations/monerium \ - --header 'Authorization: Bearer ' \ - --header 'Content-Type: application/json' \ - --data '{ - "signature": "" -}' -``` -If the request is successful, you will receive a response with the IBAN details. -This endpoint effectively: -- shares the user's KYC info with Monerium -- creates an IBAN linked to the user's account - - -Monerium only allows the user to have one single account with them. If a Monerium account **already exists**, then there is **no need** to call this endpoint. In such cases, users can grant access to their existing Monerium account using the Monerium API directly. - - - - - - -To retrieve IBAN details including the IBAN number, BIC code, status, and connected account address, you can get them with the `bankingDetails` field from: -```bash cURL -curl -X GET /api/v1/user -``` - - - - - -Follow the guide available at https://monerium.dev/docs/welcome -To authenticate with their API, you can then use the method described in https://monerium.dev/api-docs/v2#tag/auth *using the SIWE* flow. - -Once authenticated, you can manage IBANs, create instructions to transfer funds from one account to another, etc. - - - - - -For detailed error status codes and examples, please refer to the API Reference documentation [here for IBAN integration](https://docs.gnosispay.com/api-reference/iban/create-a-new-monerium-integration). - -The Gnosis Pay API serves as a wrapper for Monerium IBAN integration. Therefore, any issues related to Monerium integration should be addressed directly between your products and Monerium. - - -## Message Signing Example with Viem - -This is an example script to generate a signature from an EOA wallet address that is an owner of GP Safe and will be linked to Monerium profile. - -### Example Usage - -```typescript -import { createWalletClient, http } from 'viem'; -import { gnosis } from 'viem/chains'; -import { privateKeyToAccount } from 'viem/accounts'; - -// Initialize wallet client with private key -const account = privateKeyToAccount('0x...' as `0x${string}`); -const walletClient = createWalletClient({ - account, - chain: gnosis, - transport: http() -}); - -// Get the message to sign from the API -const message = "I hereby declare that I am the address owner."; - -// Sign the message directly with EOA -const signature = await walletClient.signMessage({ - message -}); - -console.log('Signature:', signature); -console.log('Signer address:', account.address); -``` - - -Note: -- The message can be fetched from the API using `GET /api/v1/ibans/signing-message` -- The signature can be submitted to prove ownership -- Make sure to use the correct chainId (100 for Gnosis Chain) - diff --git a/on-off-ramps/index.mdx b/on-off-ramps/index.mdx deleted file mode 100644 index b346453..0000000 --- a/on-off-ramps/index.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: "On/Off Ramp Integrations" -description: "Complete guide to fiat-to-crypto conversions with Gnosis Pay's integrated ramp partners" ---- - -# On/Off Ramp Integrations - -Convert between fiat currencies and stablecoins seamlessly with Gnosis Pay's integrated ramp partners. - - -**No Double KYC Required** - -On/Off ramp integrations don't require users to complete another KYC process with Gnosis Pay - they can use their existing verification from our integrated partners. - - - -**Important Partnership Requirements** - -Please be aware of the following integration requirements: - --**Direct Provider Relationships**: The wallet/platform must establish a direct contractual relationship with the ramp provider (e.g., Noah, Monerium, Avenia) and integrate directly with their APIs. Gnosis Pay will only facilitate the KYC process by sharing the KYC collected data via Sumsub. - -- **Mandatory usage of Sumsub**: It is mandatory to use Sumsub instance to avail these integrations. - - -For detailed KYC sharing implementation, please refer to our [KYC Sharing Integration Guide](on-off-ramps/kyc-sharing). - - -## Available Integrations - - - - **EUR to EUR.e via Monerium** - - Full SEPA integration with IBAN support for European users. - - - - **USD to USDC.e via Noah** - - Fast and reliable USD conversion for North American markets. - - - - **BRL to BRLA via Avenia** - - Brazilian Real support specifically designed for LatAm markets. - - diff --git a/on-off-ramps/kyc-sharing.mdx b/on-off-ramps/kyc-sharing.mdx deleted file mode 100644 index bc974df..0000000 --- a/on-off-ramps/kyc-sharing.mdx +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: KYC sharing for GP Partners -description: This guide explains how to share existing Gnosis Pay KYC data with GP Partners using Sumsub's reusable token system. ---- -## KYC Sharing Partners - -KYC Sharing allows users who have already completed verification with Gnosis Pay to re-use their KYC data with partner platforms, eliminating the need for duplicate verification processes. KYC sharing is currently only available through **Sumsub** and requires a formal contract between three parties: Gnosis Pay, Sumsub, and partner platform. - - -**Important Limitations** - -- **One-Way Sharing**: We only support **Gnosis Pay → Partner Platform** sharing. KYC data cannot be shared from partner platforms back to Gnosis Pay. -- **Approval Not Guaranteed**: Since every company has different KYC requirements and risk policies, some Gnosis Pay approved KYCs may be rejected at the destination platform. - - -## How to get reusable token for KYC Sharing Partners - -The following partners currently support KYC data sharing: - -| Partner | Sumsub Client ID | -|---------|------------------| -| Noah | `noah.com_112876` | -| BRLA | `brla.digital_101963` | - - - - For existing partners available for KYC sharing, use one of the Client IDs from the table above. For partners not listed in the table, contact the Partner team to obtain their SumSub Client ID. - - - -

- Once the Sumsub Client ID has been obtained, generate a reusable token using the Gnosis Pay API. -

- - ```bash cURL - curl --request POST \ - --url https://api.gnosispay.com/api/v1/kyc/import-partner-applicant \ - --header 'Authorization: Bearer ' \ - --header 'Content-Type: application/json' \ - --data '{ - "forClientId": "noah.com_112876", - "ttlInSecs": 600 - }' - ``` -
- - -

- Share this token to Sumsub's API to re-use the KYC and get data for users. To learn more about how to use Sumsub's API, visit their documentation [here](https://docs.sumsub.com/docs/reusable-kyc). -

- - ```bash cURL - curl --request POST \ - --url https://api.sumsub.com/resources/api/reusableIdentity/reuse - ``` -
-
diff --git a/onboarding-flow.mdx b/onboarding-flow.mdx deleted file mode 100644 index b8d4c65..0000000 --- a/onboarding-flow.mdx +++ /dev/null @@ -1,272 +0,0 @@ ---- -title: Onboard Users to Gnosis Pay -description: How to onboard new Users to Gnosis Pay. ---- - -Welcome! This guide will walk you through the journey to implement the complete onboarding of a user, from the first time they connect their wallet, to the point where they can order a card (physical or virtual) and start using it. - -If you want to see how it all comes together, check out our open-source [Gnosis Pay UI](https://github.com/gnosispay/ui) for a reference implementation. - -Let's jump right in! - -## 1. Authentication - -First things first: your users need to authenticate with our API. -This is covered in detail in the [Authentication Flow](/auth) section. -Once they're authenticated, you'll get a `jwt`, you'll need it for all the next steps. - -## 2. User Registration - -Now that you've got your `jwt` token, it's time to register your user in the Gnosis Pay system. - - -There are 2 ways to know whether they are already registered or not: - -- **Check the JWT:** If the decoded JWT has a `userId`, your user is already registered. -- **Or, call the user endpoint:** Try [`GET /api/v1/user`](/api-reference/user/get-the-user-profile). If you get a `401 Unauthorized Error`, the user isn't registered yet. - - - - -You can register a user with their `email` and `partnerId`, [see api documentation](/api-reference/authentication/create-a-new-user). - - -The `partnerId` is a unique identifier for your app that you can get instantly from the [Partners Dashboard](https://partners.gnosispay.com/). This is the only place where you need to include it in the request. - - -If you have a `referralCouponCode`, you can include it here too. - -```bash cURL -curl -X POST /api/v1/auth/signup \ --d '{ - "authEmail": "string", - "partnerId": "string", - "referralCouponCode": "string" (optional) -}' -``` - - - -At this point, the user is registered in the Gnosis Pay system and has an associated user id. -Users need to accept our terms of service and those from our partners. To streamline the process, you can do this step together with the previous one. - -We recommend having a mapping in your app, between the ToS id and their title and urls, so you can display the title and link out for users to read the full text. Make sure they check a box to accept before moving on! - - -This endpoint can only be called once the user is registered. Hence the proposed mapping. - - -Get the terms for the user [(spec)](/api-reference/user/retrieve-terms-and-conditions-status): -```bash cURL -curl -X GET /api/v1/user/terms -``` -We recommend you link each ID received on this endpoint with a title in your app: - -```javascript -const tosToTitle = { - "general-tos": "Gnosis Pay Terms of Service", - "card-monavate-tos": "Cardholder Terms of Service", - "cashback-tos": "Cashback Terms of Service", - "privacy-policy": "Gnosis Pay Privacy and Cookies Policy" -} -``` - -Once the user has reviewed and accepted the ToS, you can call the endpoint to accept each ToS that the user hadn't accepted yet [(spec)](/api-reference/user/accept-terms-and-conditions): -```bash cURL -curl -X POST /api/v1/user/terms \ --d '{ - "terms": "string", // e.g. "general-tos" - "version": "string" // e.g. "TOS_GENERAL_VERSION_1" -}' -``` - - - - -## 3. KYC Process - -To order a card, your users will need to go through a KYC process with our partner Sumsub. The whole KYC flow happens in a Sumsub iframe, and in the background, the Gnosis Pay API keeps you updated on your user's `kycStatus` (check it with [`GET /api/v1/user`](/api-reference/user/retrieve-user-profile) - -Here are the different KYC statuses you might see, and what they mean for your user: - -| **KYC Status** | **Description** | **What your user should do** | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------- | -| `notStarted` | No KYC process started | Start KYC | -| `documentsRequested` | Applicant must upload documents. Status moves to `pending` when done. | Upload documents | -| `pending` | Awaiting verification processing | Nothing—just wait! | -| `processing` | Profile is being processed | Nothing—system is checking | -| `approved` | All verification checks passed | Nothing—KYC is complete | -| `resubmissionRequested` | Some checks failed; user can re-submit required steps | Re-submit documents | -| `rejected` | Final rejection; user cannot try again | Nothing (final rejection) | -| `requiresAction` | Manual check required by our team | Wait or contact support | - - -You should start the KYC process if your user is registered and their `kycStatus` is `notStarted` or `documentsRequested`. - - - - -By default, each user gets a unique URL for their KYC process. -Get it with [(spec)](/api-reference/kyc/retrieve-link-for-web-integration): - -```bash cURL -curl -X GET /api/v1/kyc/integration -``` - -For mobile integrations, you can use the Sumsub Mobile SDK. -To instantiate their SDK you need an access token that you can get from [(spec)](/api-reference/kyc/retrieve-access-token-for-sumsub-sdk-integration): - -```bash cURL -curl -X GET /api/v1/kyc/integration/sdk -``` - - - - -While the whole KYC flow happens in an iframe, it is advised to constantly monitor the `kycStatus` from the user endpoint, and act if users are in certain states. - -Provide a way to contact your support if the `kycStatus` is: -- `requiresAction` -- `rejected` - -If the status is one of the following, there's nothing to do on your side: - - `pending` - - `processing` - - `resubmissionRequested` - -Finally, if the status is `approved`, you can move on to the next step. - - - - -For regulatory reasons, users need to answer a couple of questions regarding the source of funds for their card. - - -Users should be ready for this step if: -- they are registered (a `userId` is present in the JWT) -- their `kycStatus` is `approved` -- the field `isSourceOfFundsAnswered` on the user endpoint response is `false` - - -You can get the list of questions and possible answers at this endpoint [(spec)](/api-reference/kyc/retrieve-source-of-funds-questions): - -```bash cURL -curl -X GET /api/v1/source-of-funds -``` - -Show these questions to your user and collect their answers. You'll need to send all the answers together in one go [(spec)](/api-reference/kyc/answer-source-of-funds-questions): - -```bash cURL -curl -X POST /api/v1/source-of-funds \ --d '[ - { - question: "string", - answer: "string" - }, - ... -]' -``` - -Don't forget to include the question title with each answer. If you want to see how this looks in practice, check out [Gnosis Pay UI](https://github.com/gnosispay/ui). - - - - -Last but not least, your users need to verify their mobile phone. Just like with email, they'll enter their phone number, get a one-time password (OTP), and we'll verify it. - - -This step is needed if: -- The user is registered (`userId` in JWT) -- `kycStatus` is `approved` -- `isPhoneValidated` is `false` in the user endpoint response - - -Validate the phone number by first requesting an OTP [(spec)](/api-reference/kyc/retrieve-an-otp-code-to-verify-a-phone-number): - -```bash cURL -curl -X POST /api/v1/verification \ --d '{ - "phoneNumber": "string" -}' -``` - -This will send a code to the user's phone number, which in turn needs to be passed on to the check endpoint [(spec)](/api-reference/kyc/verify-a-phone-number-with-otp-code): - -```bash cURL -curl -X POST /api/v1/verification/check \ --d '{ - "code": "string" -}' -``` - -Once that's done, `isPhoneValidated` will be `true`. - - - - - -User's safe is ready for configuration if: -- They are registered (`userId` in JWT) -- `kycStatus` is `approved` -- `isSourceOfFundsAnswered` is `true` -- `isPhoneValidated` is `true` -- `safeWallet` is an empty array in the user endpoint response - - -## 4. Safe Account Configuration - -Gnosis Pay uses Safe accounts for on-chain transactions. This endpoint deploys GP Safe with currency and setup delay and roles modules for the GP Safe. - -The currency is automatically assigned based on the user's country: - -- UK users (GB): GBPe -- Brazil users (BR): USDCe -- Users from other countries: EURe (default) - - -Before this, note that the field `accountStatus` is a number that is not `0` in the response from [`GET /api/v1/safe-config`](/api-reference/safe-management/retrieve-safe-configuration). -`accountStatus: 0` would mean that the Safe modules are already deployed. - - - -```bash cURL -curl --request POST \ - --url https://api.gnosispay.com/api/v1/safe/deploy \ - --header 'Authorization: Bearer ' -``` - - If safe deployment request is successful, it will return as "accepted" from endpoint. From here it can take up to 1 minute for safe deployment and module setup. - - -## 5. Monitor Safe Deployment Status - -After initiating the GP Safe deployment, monitor the deployment progress using the [v1/safe/deploy](https://docs.gnosispay.com/api-reference/safe-management/get-the-safe-deployment-status) endpoint: - -```bash cURL -curl --request GET \ - --url https://api.gnosispay.com/api/v1/safe/deploy \ - --header 'Authorization: Bearer ' -``` - -### Verify safe configuration post deployment - -Once deployment is complete, verify the Safe is fully configured by checking the account status: - -```bash cURL -curl --request GET \ - --url https://api.gnosispay.com/api/v1/safe-config \ - --header 'Authorization: Bearer ' -``` - - -When `accountStatus` is **0**, the GP Safe is fully configured and ready for use. - - -### Using Account-Kit for Integrity Status - - Gnosis Pay Account-Kit library [`@gnosispay/account-kit`](https://github.com/gnosispay/account-kit) can provide a handy `AccountIntegrityStatus` enum. - -- `AccountIntegrityStatus.Ok`, which is `0`, means that the Safe modules are already deployed. -- `AccountIntegrityStatus.DelayQueueNotEmpty`, which is `7`, means the module is deployed correctly, but the Safe has a pending transaction, which could happen later on. - - If your interface is verifying the Safe modules deployment, both these statuses should be considered as valid. diff --git a/static/css/custom.css b/static/css/custom.css new file mode 100644 index 0000000..82143c7 --- /dev/null +++ b/static/css/custom.css @@ -0,0 +1,120 @@ +/* Gnosis Pay brand palette — using Mintlify's documented CSS hooks */ + +/* Hero headline — explicit color per theme, don't rely on inherited default */ +.hero-title { + color: #000000; +} +html.dark .hero-title { + color: #ffffff; +} + +/* Hero subtext — real color per theme instead of flat opacity (which read as washed-out grey) */ +.hero-subtext { + color: #4a4570; +} +html.dark .hero-subtext { + color: #c6c2ee; +} + +/* Page background */ +#background-color { + background-color: #fbebd4 !important; +} +html.dark #background-color { + background-color: #0b0836 !important; +} + +/* Top navbar */ +#navbar { + background-color: #1a1751 !important; +} + +/* Nav links — neon highlight on hover */ +navbar-link:hover, +nav-anchor:hover { + color: #cbfb6c !important; + transition: color 0.15s ease; +} + +/* CTA button in the topbar ("Sign up as Partner") */ +#topbar-cta-button { + background-color: #cbfb6c !important; + color: #1a1751 !important; + border: none !important; + border-radius: 999px !important; + transition: + filter 0.15s ease, + transform 0.15s ease; +} +#topbar-cta-button:hover { + filter: brightness(1.08); + transform: translateY(-1px); +} + +/* Cards — neon lime border, subtle tint, glow + lift on hover */ +/* Scoped to the individual card element/class only — NOT card-group/columns wrappers */ +card, +.card { + background-color: rgba(118, 109, 229, 0.05) !important; + border: 1px solid rgba(203, 251, 108, 0.45) !important; + border-radius: 12px !important; + box-shadow: none !important; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease, + transform 0.2s ease; +} +card:hover, +.card:hover { + border-color: #cbfb6c !important; + box-shadow: 0 0 0 3px rgba(203, 251, 108, 0.15) !important; + transform: translateY(-2px); +} + +/* Explicitly strip any border/background from the group wrapper itself */ +card-group, +columns, +.card-group { + border: none !important; + background: none !important; + box-shadow: none !important; + gap: 24px !important; +} + +/* Cards — dark mode: lighter indigo card, same neon border treatment */ +html.dark card, +html.dark .card { + background-color: rgba(255, 255, 255, 0.035) !important; + color: #fbebd4 !important; +} + +/* Footer (renders on pages without mode: custom) */ +footer { + background-color: transparent !important; + border-top: 1px solid rgba(203, 251, 108, 0.15); +} +/* Muted footer text/icons so they recede rather than compete with content */ +footer, +footer a { + color: #4a4570 !important; +} +html.dark footer, +html.dark footer a { + color: #c6c2ee !important; +} +footer a:hover { + color: #cbfb6c !important; + transition: color 0.15s ease; +} + +/* Body text — stronger contrast than Mintlify's default muted grey */ +.prose, +.prose p, +.prose li { + color: #2a2550 !important; /* deep indigo-ink on the cream background */ +} +html.dark .prose, +html.dark .prose p, +html.dark .prose li { + color: #ece9ff !important; /* near-white with a faint lilac tint */ +} diff --git a/static/img/gnosis-pay-architecture.svg b/static/img/gnosis-pay-architecture.svg new file mode 100644 index 0000000..b924a52 --- /dev/null +++ b/static/img/gnosis-pay-architecture.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + PARTNER INTEGRATION + COMPLIANCE · CUSTODY · PAYMENT PROCESSING & RAILS + + + + Sumsub + KYC & identity + + + + + + KYC + + + PARTNER + one partner · one API token + + + Onboarding API + SIWE · KYC · TOS + + + Webhooks + real-time events + + + Dashboard + B2C support + + + PSE + Sensitive card details + + + + + + + one partner = one token + + + + GNOSIS PAY + API orchestration layer + + + Account + + Card + + Processing + Settlement + + + + KYT + AML on deposit + + + Spending Safe + self-custody · funds + + + Pismo + payment processor + + + Monavate + settlement · 12h + + + Visa + card network + + + + + + + + AML screening + + + + custody + + + + processing + + + + 12h settlement + + + + card rail + + diff --git a/static/img/hero-visual.png b/static/img/hero-visual.png new file mode 100644 index 0000000..2b78c6c Binary files /dev/null and b/static/img/hero-visual.png differ diff --git a/static/img/logo-dark.svg b/static/img/logo-dark.svg new file mode 100644 index 0000000..06ba878 --- /dev/null +++ b/static/img/logo-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/static/img/logo-light.svg b/static/img/logo-light.svg new file mode 100644 index 0000000..bc7e46e --- /dev/null +++ b/static/img/logo-light.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/transactions/index.mdx b/transactions/index.mdx deleted file mode 100644 index efb377e..0000000 --- a/transactions/index.mdx +++ /dev/null @@ -1,160 +0,0 @@ ---- -title: "Card Transactions Overview" -description: "Comprehensive guide to understanding card transaction types, lifecycle, and integration patterns" ---- - -Card transactions in Gnosis Pay follow the standard payment card industry lifecycle but with unique behaviors for different transaction kinds. This guide will help you understand how transactions work, when they appear in the API, and how to handle them in your integration. - -## Transaction Kinds - -The Gnosis Pay API returns three distinct types of transaction events, each with different behaviors and timing: - -### Payment -Regular card transactions including purchases, ATM withdrawals, and other card usage. - -**Key Characteristics:** -- **Immediate visibility**: Appears in the API immediately after authorization -- **Status tracking**: Has a `status` field indicating approval, decline reasons, or reversals -- **Pending behavior**: Shows `isPending: true` when authorized but not yet settled -- **Settlement timing**: Usually settles within 24 hours, but can take up to a month for certain merchants (hotels, car rentals) - -**Common scenarios:** -- Point-of-sale purchases -- ATM cash withdrawals -- Online purchases -- Hotel pre-authorizations - -### Refund -Money being returned to the cardholder's account, typically from product returns or service cancellations. - -**Key Characteristics:** -- **Delayed visibility**: Only appears after both authorization AND clearing are processed -- **No status field**: Unlike payments, refunds don't have approval/decline status -- **Amount fields**: Includes `refundAmount` and `refundCurrency` fields -- **Pending behavior**: Can still show `isPending: true` if additional clearing steps are required - -**Common scenarios:** -- Product returns to merchants -- Service cancellations -- Merchant-initiated refunds -- Dispute resolutions - -### Reversal -Cancellation or reversal of previous transactions, often due to technical issues or merchant corrections. - -**Key Characteristics:** -- **Variable timing**: Can appear immediately (authorization-level) or after clearing -- **Amount fields**: Includes `reversalAmount` and `reversalCurrency` fields -- **Quick processing**: Usually processed faster than refunds -- **No status field**: Similar to refunds, no approval/decline status - -**Common scenarios:** -- Duplicate transaction corrections -- Technical payment processing errors -- Merchant-initiated transaction cancellations -- Partial transaction reversals - -## Transaction Lifecycle - -Understanding the transaction lifecycle is crucial for proper integration: - -### Authorization Phase -1. **Card Usage**: Customer uses card at merchant -2. **Authorization Request**: Merchant requests payment authorization -3. **Real-time Processing**: Gnosis Pay validates funds, limits, and fraud checks -4. **Response**: Authorization approved or declined -5. **API Visibility**: - - **Payments**: Immediately visible with `isPending: true` - - **Refunds**: Not yet visible (requires clearing) - - **Reversals**: May be visible if authorization-level reversal - -### Clearing Phase -1. **Settlement Processing**: Usually within 24 hours (can be longer for certain MCCs) -2. **Final Amount**: May differ from authorization amount -3. **API Updates**: - - **Payments**: `isPending` becomes `false`, `clearedAt` is set - - **Refunds**: Now become visible in the API - - **Reversals**: Update with final amounts and timing - -## Integration Best Practices - -### Handling Different Transaction Kinds - -```javascript -// Example: Processing different transaction types -transactions.forEach(transaction => { - switch (transaction.kind) { - case 'Payment': - if (transaction.isPending) { - // Handle pending payment - console.log(`Pending payment: ${transaction.billingAmount}`); - // Show as "Processing" in UI - } else { - // Handle completed payment - console.log(`Completed payment: ${transaction.status}`); - } - break; - - case 'Refund': - // Refunds are typically not pending when they appear - console.log(`Refund received: ${transaction.refundAmount}`); - // Update account balance, notify user - break; - - case 'Reversal': - // Handle transaction reversal - console.log(`Transaction reversed: ${transaction.reversalAmount}`); - // Reverse previous transaction effects - break; - } -}); -``` - -### Monitoring Transaction Status - -```javascript -// Example: Monitoring pending transactions -const monitorPendingTransactions = async () => { - const response = await fetch('/api/v1/cards/transactions'); - const data = await response.json(); - - const pendingTransactions = data.results.filter(t => t.isPending); - - if (pendingTransactions.length > 0) { - console.log(`${pendingTransactions.length} transactions pending settlement`); - // Schedule next check or set up webhooks for updates - } -}; -``` - -### Error Handling - -Different transaction kinds may have different error scenarios: - -- **Payments**: Can be declined for various reasons (insufficient funds, incorrect PIN, etc.) -- **Refunds**: Usually appear only when successfully processed -- **Reversals**: Indicate correction of previous errors - -## Common Integration Patterns - -### Real-time Balance Updates -- **Payments**: Money is immediately deducted from user account and moved to hold account on chain when `isPending: true` -- **Refunds**: Add to balance when transaction appears (usually already cleared) -- **Reversals**: Adjust balance based on reversal type and amount - -### User Notifications -- **Payments**: Notify immediately for both pending and completed -- **Refunds**: Notify when refund appears (money is being returned) -- **Reversals**: Notify about transaction corrections - -### Transaction History Display -- Show transaction kind clearly in the UI -- Use `isPending` status for appropriate visual indicators -- Handle currency conversions (billing vs transaction currency) -- Display appropriate amounts based on transaction kind - -## Next Steps - -- [Transaction Lifecycle Details](/transactions/lifecycle) - Deep dive into authorization and clearing -- [API Integration Guide](/transactions/integration-guide) - Complete integration examples -- [API Reference](/api-reference/transactions/list-card-transactions) - Full API documentation diff --git a/transactions/integration-guide.mdx b/transactions/integration-guide.mdx deleted file mode 100644 index c2d5b0f..0000000 --- a/transactions/integration-guide.mdx +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: "Transaction Integration Guide" -description: "Complete examples and best practices for integrating with Gnosis Pay transaction APIs" ---- - -This guide provides comprehensive examples and patterns for integrating with Gnosis Pay's transaction APIs, covering all three transaction kinds and their unique behaviors. - -## Basic Integration Setup - -### Authentication and API Setup - - - -```javascript JavaScript -const GNOSIS_PAY_API = 'https://api.gnosispay.com'; - -class GnosisPayClient { - constructor(authToken) { - this.authToken = authToken; - } - - async getTransactions(params = {}) { - const queryParams = new URLSearchParams(params); - const response = await fetch( - `${GNOSIS_PAY_API}/api/v1/cards/transactions?${queryParams}`, - { - headers: { - 'Authorization': `Bearer ${this.authToken}`, - 'Content-Type': 'application/json' - } - } - ); - - if (!response.ok) { - throw new Error(`API Error: ${response.status}`); - } - - return response.json(); - } -} -``` - -```python Python -import requests -from typing import Dict, Any, Optional - -class GnosisPayClient: - def __init__(self, auth_token: str): - self.auth_token = auth_token - self.base_url = "https://api.gnosispay.com" - - def get_transactions(self, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - headers = { - "Authorization": f"Bearer {self.auth_token}", - "Content-Type": "application/json" - } - - response = requests.get( - f"{self.base_url}/api/v1/cards/transactions", - headers=headers, - params=params or {} - ) - - response.raise_for_status() - return response.json() -``` - -```curl cURL -# Get all transactions -curl -X GET "https://api.gnosispay.com/api/v1/cards/transactions" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -H "Content-Type: application/json" - -# Get transactions with filters -curl -X GET "https://api.gnosispay.com/api/v1/cards/transactions?limit=50&after=2024-01-01T00:00:00Z" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -H "Content-Type: application/json" -``` - - - -## Handling Different Transaction Kinds - -### Transaction Processing Logic - - - -```javascript JavaScript -class TransactionProcessor { - processTransactions(transactions) { - const processed = { - payments: { pending: [], completed: [] }, - refunds: [], - reversals: [] - }; - - transactions.forEach(transaction => { - switch (transaction.kind) { - case 'Payment': - this.processPayment(transaction, processed); - break; - case 'Refund': - this.processRefund(transaction, processed); - break; - case 'Reversal': - this.processReversal(transaction, processed); - break; - default: - console.warn('Unknown transaction kind:', transaction.kind); - } - }); - - return processed; - } - - processPayment(payment, processed) { - if (payment.isPending) { - processed.payments.pending.push({ - ...payment, - displayStatus: 'Processing', - balanceImpact: -parseInt(payment.billingAmount), // Already deducted - canCancel: this.canCancelPayment(payment) - }); - } else { - processed.payments.completed.push({ - ...payment, - displayStatus: this.getPaymentDisplayStatus(payment), - balanceImpact: 0, // Already accounted for when pending - settlementDate: payment.clearedAt - }); - } - } - - processRefund(refund, processed) { - processed.refunds.push({ - ...refund, - displayStatus: 'Refund Processed', - balanceImpact: parseInt(refund.refundAmount), // Money returned - refundDate: refund.clearedAt, - originalTransaction: this.findOriginalTransaction(refund) - }); - } - - processReversal(reversal, processed) { - processed.reversals.push({ - ...reversal, - displayStatus: 'Transaction Reversed', - balanceImpact: parseInt(reversal.reversalAmount), // Money returned - reversalDate: reversal.clearedAt, - reversalReason: this.determineReversalReason(reversal) - }); - } - - getPaymentDisplayStatus(payment) { - switch (payment.status) { - case 'Approved': return 'Completed'; - case 'IncorrectPin': return 'Failed - Incorrect PIN'; - case 'InsufficientFunds': return 'Failed - Insufficient Funds'; - case 'Reversal': return 'Reversed'; - case 'PartialReversal': return 'Partially Reversed'; - default: return 'Completed'; - } - } -} -``` - -```python Python -from dataclasses import dataclass -from typing import List, Dict, Any -from datetime import datetime - -@dataclass -class ProcessedTransaction: - original: Dict[str, Any] - display_status: str - balance_impact: int - additional_info: Dict[str, Any] - -class TransactionProcessor: - def process_transactions(self, transactions: List[Dict[str, Any]]) -> Dict[str, List]: - processed = { - "payments": {"pending": [], "completed": []}, - "refunds": [], - "reversals": [] - } - - for transaction in transactions: - kind = transaction.get("kind") - - if kind == "Payment": - self._process_payment(transaction, processed) - elif kind == "Refund": - self._process_refund(transaction, processed) - elif kind == "Reversal": - self._process_reversal(transaction, processed) - else: - print(f"Warning: Unknown transaction kind: {kind}") - - return processed - - def _process_payment(self, payment: Dict[str, Any], processed: Dict): - if payment.get("isPending"): - processed["payments"]["pending"].append( - ProcessedTransaction( - original=payment, - display_status="Processing", - balance_impact=-int(payment["billingAmount"]), - additional_info={ - "can_cancel": self._can_cancel_payment(payment), - "estimated_settlement": self._estimate_settlement_time(payment) - } - ) - ) - else: - processed["payments"]["completed"].append( - ProcessedTransaction( - original=payment, - display_status=self._get_payment_display_status(payment), - balance_impact=0, # Already accounted for when pending - additional_info={ - "settlement_date": payment.get("clearedAt") - } - ) - ) - - def _get_payment_display_status(self, payment: Dict[str, Any]) -> str: - status_map = { - "Approved": "Completed", - "IncorrectPin": "Failed - Incorrect PIN", - "InsufficientFunds": "Failed - Insufficient Funds", - "Reversal": "Reversed", - "PartialReversal": "Partially Reversed" - } - return status_map.get(payment.get("status"), "Completed") -``` - - - - -## Next Steps - -- [Transaction Lifecycle](/transactions/lifecycle) - Understand the detailed flow -- [API Reference](/api-reference/transactions/list-card-transactions) - Complete API documentation -- [Transaction Overview](/transactions) - Back to main guide diff --git a/transactions/lifecycle.mdx b/transactions/lifecycle.mdx deleted file mode 100644 index 9d36516..0000000 --- a/transactions/lifecycle.mdx +++ /dev/null @@ -1,195 +0,0 @@ ---- -title: "Transaction Lifecycle" -description: "Deep dive into the authorization and clearing process for card transactions" ---- - -Understanding the transaction lifecycle is essential for building robust integrations with Gnosis Pay. This guide explains the detailed flow from card usage to final settlement. - -## Overview - -Card transactions follow a two-phase process: - -1. **Authorization Phase**: Real-time approval/decline of the transaction -2. **Clearing Phase**: Final settlement and fund transfer (usually 24-48 hours later) - -Different transaction kinds have different behaviors in each phase. - -## Authorization Phase - -### Payment Authorization - -When a customer uses their Gnosis Pay card: - - - - Customer presents card at merchant (physical swipe/insert or online entry) - - - Merchant's payment processor sends authorization request to Gnosis Pay - - - Gnosis Pay performs multiple checks: - - Available balance verification - - Card status (active, not frozen) - - Spending limits compliance - - Anti-fraud analysis - - AML screening - - - Gnosis Pay responds with approval or decline - - - If approved, money is immediately deducted from user account and moved to hold account on chain - - - Transaction appears immediately in `/api/v1/cards/transactions` with: - - `kind: "Payment"` - - `isPending: true` - - `clearedAt: null` - - `status: "Approved"` (or decline reason) - - - -### Refund Authorization - -Refunds have a different authorization flow: - - - - Merchant initiates refund (product return, service cancellation) - - - Merchant sends refund authorization request - - - Gnosis Pay validates the refund request - - - **Important**: Refund does NOT appear in API yet - requires clearing - - - -### Reversal Authorization - -Reversals can happen at the authorization level: - - - - System or merchant detects transaction error - - - Immediate reversal request sent - - - Original authorization is reversed - - - May appear immediately as reversal or update original payment status - - - -## Clearing Phase - -The clearing phase typically occurs 24-48 hours after authorization, but timing varies by merchant type. - -### Standard Clearing Timeline - -- **Most merchants**: 24-48 hours -- **Hotels**: Can be up to 30 days (for incidentals) -- **Car rentals**: Up to 30 days -- **Airlines**: Usually 24-48 hours -- **Gas stations**: Often same day - -### Payment Clearing - - - - Merchant submits clearing record (usually batch processed overnight) - - - Clearing amount may differ from authorization: - - **Exact match**: Most common scenario - - **Partial capture**: Lower amount (unused authorization released) - - **Over capture**: Higher amount (allowed for specific MCCs like hotels) - - - Money is transferred from hold account on chain to merchant - - - Transaction updates in API: - - `isPending: false` - - `clearedAt: "2024-01-15T02:30:00Z"` - - Amounts may be updated if different from authorization - - - -### Refund Clearing - -Refunds require both authorization AND clearing to appear: - - - - Merchant submits credit voucher (BaseII transaction code "06") - - - System validates refund against original transaction - - - Funds are credited back to cardholder account - - - Refund now appears in API: - - `kind: "Refund"` - - `isPending: false` (usually) - - `clearedAt: "2024-01-15T08:20:00Z"` - - `refundAmount` and `refundCurrency` fields - - - - -## Edge Cases and Special Scenarios - -### Partial Authorizations and Captures - -Some merchants may: -- **Partial capture**: Capture less than authorized amount -- **Multi capture**: Multiple captures on single authorization -- **Incremental authorization**: Additional authorizations (hotels for incidentals) - -### Force Captures - -Rare but possible scenarios: -- **Offline transactions**: POS terminals without internet (airlines) -- **Force capture**: Capture on previously declined authorization - -### Authorization Expiry - -If not captured within time limits: -- Authorization expires (typically 7-30 days) -- Money is returned from hold account back to user account on chain -- Transaction may disappear from API or show as expired - -## Webhook Integration - -For real-time updates, consider implementing webhooks to be notified when: -- Pending payments are settled -- Refunds are processed -- Reversals occur - -## Troubleshooting Common Issues - -### Missing Transactions -- **Payments not appearing**: Check authorization was approved -- **Refunds not appearing**: Ensure both authorization and clearing completed -- **Delayed settlements**: Some MCCs have extended clearing times - - -### Status Confusion -- **isPending behavior**: Different meanings for different transaction kinds -- **Timing expectations**: Clearing can take hours to days depending on merchant - -## Next Steps - -- [Integration Guide](/transactions/integration-guide) - Complete implementation examples -- [API Reference](/api-reference/transactions/list-card-transactions) - Full API documentation -- [Transaction Overview](/transactions) - Back to main transactions guide diff --git a/cards/assets/css-styling.png b/v2/cards/assets/css-styling.png similarity index 100% rename from cards/assets/css-styling.png rename to v2/cards/assets/css-styling.png diff --git a/v2/cards/v2-pse-integration.mdx b/v2/cards/v2-pse-integration.mdx new file mode 100644 index 0000000..39ceb5b --- /dev/null +++ b/v2/cards/v2-pse-integration.mdx @@ -0,0 +1,529 @@ +--- +title: Partner Secure Elements (PSE) Integration +description: Display sensitive card information and manage PINs securely in your client application using PSE SDK (PSE version 3 with EIP-712 two-factor authentication). +--- + +## Overview + +If you want to display sensitive information (such as card numbers or PINs) in your front-end, you'll need to interact with our **Partner Secure Elements (PSE)** service. The easiest way to do this is by using the [PSE SDK](https://www.npmjs.com/package/@gnosispay/pse-sdk) from Gnosis Pay. + +To initialize the SDK, you'll need: +- An `App ID` provided to you upon registration with Gnosis Pay +- An **ephemeral-token** retrieved from the PSE private API using mTLS authentication +- An **auth module token** obtained through the [SIWE authentication flow](/v2/v2-siwe-auth) +- An **EIP-712 signature** and **nonce** obtained from the challenge flow described below + + + **PSE version 3** adds an EIP-712 wallet signature as a second factor for every sensitive operation. Pass `pseVersion: 3` when constructing the SDK and include `eip712Signature` and `eip712Nonce` obtained from the challenge flow described below. + + Note: `pseVersion` refers to the PSE SDK protocol version and is unrelated to the Gnosis Pay API path (which remains `/v2`). + + + + **Backend Required**: mTLS authentication can only be performed from a + back-end. You need a back-end responsible for retrieving the + ephemeral-token and sending it to your front-end upon request. We'll go + through each step in this guide. + + +Once your front-end has the ephemeral-token and auth module token, it can initialize the PSE SDK and use it to display secure elements. + +Here's a diagram showing each step: + +```mermaid + sequenceDiagram + participant FE as Your Frontend + participant BE as Your Backend + participant Auth as Auth Module + participant PSE as PSE API + + FE->>Auth: SIWE Login + Auth-->>FE: authModuleToken (15min access token) + + FE->>BE: Request ephemeral-token + BE->>PSE: POST /api/v1/ephemeral-token (mTLS) + PSE-->>BE: ephemeral-token + BE-->>FE: ephemeral-token + + FE->>Auth: GET /pci/cards/{cardId}/challenge?action=... + Auth-->>FE: EIP-712 typed data (domain, types, message, nonce) + Note over FE: User signs typed data in wallet + + Note over FE: Initialize PSE SDK (PSE version 3) with
ephemeralToken, authModuleToken, appId,
eip712Signature, eip712Nonce + + FE->>PSE: SDK requests card data + PSE->>Auth: Verifies EIP-712 signature + nonce + Auth-->>PSE: Signature valid + PSE-->>FE: Renders card data in secure iframe +``` + +--- + +## Secure Connection Using mTLS Authentication + +**Mutual TLS (mTLS)** is a type of authentication in which two parties in a connection authenticate each other using the TLS protocol. Your back-end will establish an mTLS authentication with the Gnosis Pay private PSE API to receive an ephemeral-token. + +### How to Generate mTLS Certificates + +After signing up through the [Partners Dashboard](https://partners.gnosispay.com/), you will receive an `App ID` instantly, which will be used in the certificate generation below. You must first create a private key and then generate a Certificate Signing Request (CSR) using the `App ID` as follows: + +```graphql +# APP_ID is a string starting with `gp_` that you have received from Gnosis Pay +export APP_ID="gp_woop_123" + +# Create a private key (NEVER share with anyone) +openssl ecparam -name prime256v1 -genkey -noout -out "${APP_ID}.key.pem" + +# Create the CSR (OK to share) +openssl req -new -sha256 -key "${APP_ID}.key.pem" -out "${APP_ID}.csr.pem" -subj "/CN=${APP_ID}" +``` + +You can now share the `${APP_ID}.csr.pem` file with the Gnosis Pay team. **DO NOT EVER** share the `.key.pem` file with **ANYONE**. + +Once we receive your Certificate Signing Request, we will sign it and send you back the signed certificates. These signed certificates, along with your private key, are used to establish the connection with the PSE API. + +### How to Establish an mTLS Authentication (in Node.js) + +You should securely store the certificates in your environment along with your private key. + +Your environment should expose the certificates and private key, for example: + +```rust +SIGNED_CERTIFICATES="-----BEGIN CERTIFICATE----- +ABCQz .... +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +DEFC7 .... +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +GHICc .... +-----END CERTIFICATE-----" + +PRIVATE_KEY="-----BEGIN EC PRIVATE KEY----- +ABCD.... +-----END EC PRIVATE KEY-----" +``` + +Here's a Node.js implementation to request the ephemeral-token in two different ways: + + + +```js Using Axios +const httpsAgent = new https.Agent({ + cert: process.env.SIGNED_CERTIFICATES, + key: process.env.PRIVATE_KEY, + rejectUnauthorized: true, // Ensure SSL verification +}); + +const ephemeralTokenRequest = await axios({ + httpsAgent: httpsAgent, + method: "POST", + url: `https://api-pse.gnosispay.com/api/v1/ephemeral-token`, + headers: { "Content-Type": "application/json" }, + // Axios adds the user-agent automatically +}); +``` + +```js Using https.request +import https from "https"; + +const httpsAgent = new https.Agent({ + cert: CERT, + key: KEY, + rejectUnauthorized: true, +}); + +const req = https.request( + { + hostname: "api-pse.gnosispay.com", + path: "/api/v1/ephemeral-token", + method: "POST", + headers: { + "Content-Type": "application/json", + "User-Agent": "User-Client/1.0.0", + }, + agent: httpsAgent, + }, + (res) => { + let data = ""; + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + console.log("Status:", res.statusCode); + + try { + const parsedData = JSON.parse(data); + console.log("Response:", parsedData); + } catch { + console.log("Raw response:", data); + } + }); + } +); + +req.on("error", (error) => { + console.error("Request error:", error); +}); +req.end(); +``` + + + + + The ephemeral-token, as its name suggests, is valid for a very short time + frame. It is advised to generate a new one for every usage of the SDK. + + +--- + +## EIP-712 Two-Factor Authentication + +PSE version 3 requires a wallet signature before each sensitive operation. This acts as a second factor: even if an `authModuleToken` is compromised, an attacker cannot view card data or change a PIN without also controlling the user's wallet. + +### How It Works + +Before initializing the SDK, your front-end must: + +1. Request a one-time **challenge** from the auth module. +2. Ask the user's wallet to **sign** the returned EIP-712 typed data. +3. Pass the resulting **signature** and **nonce** to the SDK constructor. + +The PSE service forwards both values to the auth module, which verifies the signature and marks the nonce as consumed. Each challenge is **single-use** and **expires after 5 minutes**. + +### The Challenge Endpoint + +``` +GET /pci/cards/{cardId}/challenge?action={action} +Authorization: Bearer {authModuleToken} +``` + +The `action` parameter determines which operation is being authorized: + +| `action` | Operation | +|----------|-----------| +| `view-details` | Display card number, expiry, and security code | +| `view-pin` | Display the current PIN | +| `change-pin` | Set or change the PIN | + +The response is a complete EIP-712 typed data object, ready to pass directly to `signTypedData`: + +```json +{ + "domain": { + "name": "Display VISA Card Details", + "version": "1", + "chainId": 42220 + }, + "primaryType": "Message", + "types": { + "Message": [ + { "name": "authorization", "type": "string" }, + { "name": "nonce", "type": "uint256" } + ] + }, + "message": { + "authorization": "Display VISA Card Details", + "nonce": "123456789012345678901234567890" + } +} +``` + + + `message.nonce` is returned as a decimal string representing a `uint256`. + Convert it to `BigInt` before passing it to `signTypedData`. + + +### Signing the Challenge + + + +```typescript Using wagmi +import { useSignTypedData } from "wagmi"; + +const { signTypedDataAsync } = useSignTypedData(); + +// 1. Fetch the challenge (authModuleToken is sent automatically via client config) +const { data, error } = await getPciCardsByCardIdChallenge({ + path: { cardId }, + query: { action: "view-details" }, +}); + +if (error || !data) throw new Error("Failed to get EIP-712 challenge"); + +// 2. Sign — convert nonce to BigInt for the uint256 type +const signature = await signTypedDataAsync({ + domain: data.domain, + types: data.types, + primaryType: data.primaryType, + message: { + authorization: data.message.authorization, + nonce: BigInt(data.message.nonce), + }, +}); + +// 3. Pass to SDK constructor +const eip712Signature = signature; +const eip712Nonce = data.message.nonce; // keep as decimal string +``` + +```typescript Using viem directly +import { createWalletClient, custom } from "viem"; +import { celo } from "viem/chains"; + +const walletClient = createWalletClient({ + chain: celo, + transport: custom(window.ethereum), +}); + +// 1. Fetch the challenge +const response = await fetch( + `/pci/cards/${cardId}/challenge?action=view-details`, + { headers: { Authorization: `Bearer ${authModuleToken}` } } +); +const data = await response.json(); + +// 2. Sign +const [account] = await walletClient.getAddresses(); +const signature = await walletClient.signTypedData({ + account, + domain: data.domain, + types: data.types, + primaryType: data.primaryType, + message: { + authorization: data.message.authorization, + nonce: BigInt(data.message.nonce), + }, +}); + +const eip712Signature = signature; +const eip712Nonce = data.message.nonce; +``` + + + + + The challenge must be fetched **immediately before** each SDK initialization. + Do not reuse a nonce across different operations or SDK instances — it will be + rejected after the first use. + + +--- + +## How to Use the PSE SDK + +### Installation + +```bash +npm install @gnosispay/pse-sdk +``` + +### Backend: Ephemeral Token Relay + +Your backend needs an endpoint that proxies ephemeral token requests to the PSE private API using mTLS. Here's an example using Express: + +```js +import express from "express"; +import axios from "axios"; +import https from "node:https"; + +const app = express(); + +app.get("/api/ephemeral-token", async (_req, res) => { + try { + const cert = Buffer.from(process.env.CLIENT_CERT, "base64").toString("ascii"); + const key = Buffer.from(process.env.CLIENT_KEY, "base64").toString("ascii"); + const httpsAgent = new https.Agent({ cert, key, rejectUnauthorized: true }); + + const response = await axios({ + method: "POST", + url: "https://api-pse.gnosispay.com/api/v1/ephemeral-token", + headers: { "Content-Type": "application/json" }, + httpsAgent, + }); + + res.json({ data: response.data.data }); + } catch (error) { + res.status(502).json({ error: "Failed to reach PSE private API" }); + } +}); +``` + +In this example, `CLIENT_CERT` and `CLIENT_KEY` are the base64-encoded signed certificate and private key stored in your environment variables. + +### Frontend: Initialize the SDK + +After completing the EIP-712 challenge/sign step above, initialize the SDK with `pseVersion: 3`: + +```typescript +import GPSDK, { ElementType } from "@gnosispay/pse-sdk"; + +// 1. Get the auth module access token from your SIWE auth flow +const authModuleToken = getAccessToken(); // your auth implementation + +// 2. Fetch ephemeral token from your backend +const response = await fetch("/api/ephemeral-token"); +const { data } = await response.json(); + +// 3. Fetch and sign an EIP-712 challenge (see section above) +// Use the action matching the operation you are about to perform: +// "view-details" | "view-pin" | "change-pin" +const { eip712Signature, eip712Nonce } = await getSignedChallenge(cardId, "view-details"); + +// 4. Initialize the SDK with PSE version 3 +const gpSdk = new GPSDK({ + pseVersion: 3, // PSE version 3 — required for EIP-712 2FA + appId: "gp_your_app_id", // Your Partner App ID + ephemeralToken: data.ephemeralToken, + authModuleToken: authModuleToken, // Auth module access token + eip712Signature: eip712Signature, // Hex signature from wallet + eip712Nonce: eip712Nonce, // Decimal nonce string from challenge + onActionSuccess: (action) => { + console.log("Action completed:", action); + }, + onInvalidToken: (message) => { + console.error("Token invalid:", message); + // Refresh the ephemeral token and reinitialize + }, + onError: (message, details) => { + console.error("PSE error:", message, details); + }, +}); +``` + + + The `authModuleToken` expires every 15 minutes. Handle the `onInvalidToken` + callback to refresh your access token via the [token refresh flow](/v2/v2-siwe-auth) + and re-initialize the SDK. + + +### Display Card Details + +Use `ElementType.CardData` to display the full card number, expiration date, and security code. + + + Use `action: "view-details"` when fetching the EIP-712 challenge for this element. + + +```typescript +// cardId is a UUID obtained from GET /cards +const { destroy } = gpSdk.init(ElementType.CardData, "#card-data-container", { + cardId: "019c9578-a8ce-7445-9961-51b945605f70", +}); + +// Call destroy() when unmounting or cleaning up +destroy(); +``` + +### View Card PIN + +Use `ElementType.CardPin` to display the card's current PIN inside the secure iframe. + + + Use `action: "view-pin"` when fetching the EIP-712 challenge for this element. + + +```typescript +// Fetch and sign the challenge with action "view-pin" before constructing the SDK +const { eip712Signature, eip712Nonce } = await getSignedChallenge(cardId, "view-pin"); + +const gpSdk = new GPSDK({ + pseVersion: 3, + appId: "gp_your_app_id", + ephemeralToken: data.ephemeralToken, + authModuleToken: authModuleToken, + eip712Signature, + eip712Nonce, + onActionSuccess: (action) => { /* ... */ }, + onInvalidToken: (message) => { /* ... */ }, + onError: (message, details) => { /* ... */ }, +}); + +const { destroy } = gpSdk.init(ElementType.CardPin, "#pin-container", { + cardId: "019c9578-a8ce-7445-9961-51b945605f70", +}); +``` + +### Set / Change Card PIN + +Use `ElementType.SetCardPin` to render a PIN entry form that lets the cardholder set or change their PIN. + + + Use `action: "change-pin"` when fetching the EIP-712 challenge for this element. + + +```typescript +// Fetch and sign the challenge with action "change-pin" before constructing the SDK +const { eip712Signature, eip712Nonce } = await getSignedChallenge(cardId, "change-pin"); + +const gpSdk = new GPSDK({ + pseVersion: 3, + appId: "gp_your_app_id", + ephemeralToken: data.ephemeralToken, + authModuleToken: authModuleToken, + eip712Signature, + eip712Nonce, + onActionSuccess: (action) => { + // Fired with Action.SetPin when the user submits the new PIN, + // and Action.DoneSettingPin when the operation is confirmed. + console.log("PIN operation completed:", action); + }, + onInvalidToken: (message) => { /* ... */ }, + onError: (message, details) => { /* ... */ }, +}); + +const { destroy } = gpSdk.init(ElementType.SetCardPin, "#set-pin-container", { + cardId: "019c9578-a8ce-7445-9961-51b945605f70", +}); +``` + +### Refresh Ephemeral Token + +If you need to refresh the ephemeral token without re-creating the SDK instance: + +```typescript +const newToken = await fetchNewEphemeralToken(); +gpSdk.refreshToken(newToken); +``` + +### Callbacks + +The SDK provides three callbacks to handle events from the iframe: + +| Callback | Trigger | +|----------|---------| +| `onActionSuccess(action)` | A user action completes (e.g., `CardNumberCopied`, `SetPin`, `DoneSettingPin`) | +| `onInvalidToken(message)` | The ephemeral token has expired or is invalid | +| `onError(message, details)` | An error occurred inside the iframe | + +--- + +## How to Customize the Style of Secure Elements in the iframe + +For security reasons, the only way to apply custom styling to iframe elements is to prepare and share a **CSS file** with the Gnosis Pay team. This file, named `.css`, will be incorporated into the iframe. + +Standard styling is applied to the iframe elements by default. You can override the style of these classes and IDs as needed. Here are some of them: + +#### Card Data + +- `.pse-container` - A shared class for all iframe containers. +- `#pse-card-data-container` - The main container for displaying card data. +- `.pse-card-field` - The container for each card data field (card number, expiry date, security code). +- `.pse-card-label` - Labels for each field. +- `.pse-card-value` - The container for the actual card data values. + + +--- + +### Styling Guide + +Here is a suggested workflow to customize the styling: + +1. In your front-end, load the element you wish to customize (e.g., the card data). +1. Locate the custom CSS file with your name in either the "**Style Editor**" in Firefox or the "**Sources**" panel on Chrome/Brave. In the example below, the file is `gnosis_pay_ui.css`. +1. Apply your desired styling. The changes will be reflected in your interface immediately. +1. Save the file and send it to Gnosis Pay for application in production. + +Here is an example of overriding the `.pse-card-field` class in Firefox: +![custom css](./assets/css-styling.png) \ No newline at end of file diff --git a/v2/cards/v2-simulate-card.mdx b/v2/cards/v2-simulate-card.mdx new file mode 100644 index 0000000..8dbec44 --- /dev/null +++ b/v2/cards/v2-simulate-card.mdx @@ -0,0 +1,323 @@ +--- +title: Simulate Card Transactions +description: Simulate card transactions for testing purposes as part of sandbox environment. +--- + +### Card Transaction Simulator + +The Card Transaction Simulator allows you to test full card transaction lifecycles in the sandbox environment — without sending traffic to the live card network. + +When you send a transaction request: + +1. The simulator authenticates the request +2. It checks available balance +3. It applies lifecycle logic: + - **Authorization** creates a hold + - **Reversal** releases a hold + - **Replacement** adjusts an existing hold + - **Clearing** confirms/settles a transaction + - **Clearing Cancellation** cancels via clearing +4. It returns an approval or denial response with production-like fields + +**Important:** No traffic is sent to the live card network. All validation and balance logic is handled internally by the simulator. + +``` +https://core.sandbox.gnosispay.in/docs/simulator-api +``` + +## How to Simulate a Card Transaction + + + +Use HTTP Basic Auth: +- **Username:** `simulator` +- **Password:** Provided during onboarding + +All simulator endpoints require authentication. + + + +You can simulate transactions using: +- `card_id` → `POST /transactions/simulate` +- `pan` → `POST /transactions/simulate-by-pan` + +Both endpoints behave identically. + + + +All simulation requests use these fields: + +| Field | Type | Required | Description | +|---|---|---|---| +| `transaction_type` | string | Yes | `authorization`, `reversal`, or `replacement` | +| `card_id` | uuid | Yes* | Card UUID (use `pan` instead on the by-PAN endpoint) | +| `pan` | string | Yes* | Card PAN (only on the by-PAN endpoint) | +| `amount` | string | Yes | Amount in major units (e.g., `"100.50"`) | +| `currency` | string | Yes | `USD`, `EUR`, or `GBP` | +| `authorization_code` | string | Conditional | Required for `reversal` and `replacement` | +| `replacement_amount` | string | Conditional | Required for `replacement` | +| `merchant_name` | string | No | Defaults to `"TEST MERCHANT"` | +| `merchant_code` | string | No | MCC code. Defaults to `"5541"` | + + + + +## Transaction Types + +The simulator supports five transaction types that form a complete lifecycle: + +## Simulate an Authorization + +Authorization creates an initial hold on the card balance. + + + +```json +{ + "transaction_type": "authorization", + "card_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "amount": "100.50", + "currency": "USD" +} +``` + +**Required:** +- `transaction_type`: "authorization" +- `amount` +- `currency` +- `card_id` or `pan` + + + +The simulator will: +- Confirm the card exists +- Verify the card is active +- Check sufficient available balance +- Place a hold for the requested amount + + + +If approved, the response includes: +- `authorization_code` +- `authorization_id` + +**Save the `authorization_code`** — it is required for reversals, replacements, and clearing operations. + + + +## Simulate a Reversal + +Reversal cancels a previous authorization entirely and releases the held amount. + + + +You must use the `authorization_code` returned from the original authorization. + + + +```json +{ + "transaction_type": "reversal", + "card_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "amount": "100.50", + "currency": "USD", + "authorization_code": "ABC123" +} +``` + + + +The simulator will: +- Validate the original authorization exists +- Confirm the amount matches +- Release the full held balance + + + +## Simulate a Replacement + +Replacement adjusts a previous authorization to a different amount. + + + +Use the `authorization_code` from the original authorization. + + + +```json +{ + "transaction_type": "replacement", + "card_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "amount": "100.50", + "currency": "USD", + "authorization_code": "ABC123", + "replacement_amount": "75.00" +} +``` + +**Required:** +- `authorization_code` +- `replacement_amount` + + + +The simulator will: +- Validate the original authorization +- Reduce or increase the held amount +- Update the balance accordingly + + + +## Simulate a Clearing + +Clearing confirms a previous authorization (settlement/base II) and finalizes the transaction. + + + +Use the `authorization_code` from the original authorization. + + + +```json +{ + "transaction_type": "clearing", + "card_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "amount": "100.50", + "currency": "USD", + "authorization_code": "ABC123" +} +``` + + + +The simulator will: +- Validate the original authorization +- Process the settlement +- Finalize the transaction + + +The simulator requires a minimum 2-minute gap between clearing messages for the same authorization. Sending a second clearing before this window elapses will result in an error. + + + + +## Simulate a Clearing Cancellation + +Clearing cancellation cancels a transaction via clearing. For partial cancellation, send an amount lower than the original authorization. + + + +Use the `authorization_code` from the original authorization. + + + +```json +{ + "transaction_type": "clearing_cancellation", + "card_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "amount": "100.50", + "currency": "USD", + "authorization_code": "ABC123" +} +``` + + + +The simulator will: +- Validate the original authorization +- Process the cancellation +- Adjust balances accordingly + + +For partial cancellations, use an amount lower than the original authorization amount. + + + + +## Response Handling + +### Approved (Authorization) +An approved authorization response includes full transaction details: + +```json +{ + "status": "approved", + "authorization_code": "ABC123", + "authorization_id": 98765432, + "authorization_date_time": "2025-06-15T14:30:00Z", + "settlement_amount": "100.50", + "mode_type": "DEBIT", + "card_mode": "CHIP", + "is_domestic_transaction": true +} +``` + +### Approved (Clearing/Clearing Cancellation) +Clearing responses are sparse — most fields will be empty or zero-valued: + +```json +{ + "status": "approved", + "authorization_code": "", + "authorization_id": 0, + "authorization_date_time": "0001-01-01T00:00:00Z", + "settlement_amount": "", + "mode_type": "", + "card_mode": "", + "is_domestic_transaction": false +} +``` + +### Denied +If denied, the response includes detailed denial information: + +```json +{ + "status": "denied", + "authorization_code": "", + "authorization_id": 98765432, + "authorization_date_time": "2025-06-15T14:30:00Z", + "settlement_amount": "0.00", + "mode_type": "DEBIT", + "card_mode": "CHIP", + "is_domestic_transaction": true, + "denial_code": "810", + "denial_reason": "Insufficient balance" +} +``` + +## Common Denial Codes + +| Code | Reason | +|---|---| +| `810` | Insufficient balance | +| `UBT` | Card is blocked | +| `BNP` | Card reported lost | +| `BNR` | Card reported stolen | +| `FRB` | Card not activated | +| `BND` | Card is cancelled | +| `PFT` | Denied by anti-fraud | +| `FR7` | Card not present in database | +| `VNM` | Card expired | +| `CNC` | Account cancelled | +| `CND` | Account blocked | + +## Error Handling + +Validation and authentication errors return structured error objects: + +```json +{ + "code": 400, + "message": "authorization_code is required for reversal, replacement, and clearing transactions" +} +``` + +**Possible HTTP status codes:** + +| Status | Cause | +|--------|-------| +| `401` | Invalid credentials | +| `400` | Validation error (missing fields, invalid amount/currency) | +| `404` | Card or account not found | +| `409` | Clearing collision — a clearing message is already being processed for this authorization. Wait at least 2 minutes between clearing messages | diff --git a/v2/cards/v2-virtual-card.mdx b/v2/cards/v2-virtual-card.mdx new file mode 100644 index 0000000..23e37c2 --- /dev/null +++ b/v2/cards/v2-virtual-card.mdx @@ -0,0 +1,44 @@ +--- +title: Create Virtual Card +description: Create and Manage Virtual Cards for Users. +--- + +Virtual cards are activated immediately after creation and can be used for online purchases right away. + +## Card Activation + +To create a card for a user, call [`POST /cards/virtual`](/api-reference/cards/create-virtual-card) with a `cardName`. Once successful, you will get a `cardId`. +The card's `status` will first be `provisioning` until our card provider has fully created the card. This is an asynchronous task. We recommend showing this `status` to your users, and polling the `/cards` endpoint regularly until it becomes `active`. The provisionning process should not last most than 10s. + +The request accepts an optional `phone` field (E.164 format). The behaviour depends on whether the user already has a `cardholderId`: + +| Scenario | Result | +|----------|--------| +| User already has a `cardholderId` | Card created directly; `phone` is ignored | +| No `cardholderId`, no `phone` provided | `422 PHONE_REQUIRED` | +| No `cardholderId`, valid `phone` provided | Cardholder created via Core API, then card created | + + + +```bash +curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/cards/virtual \ + --header 'Content-Type: application/json' \ + --data '{ + "cardName": "", + "phone": "" +}' +``` + + +```bash +curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/cards/virtual \ + --header 'Content-Type: application/json' \ + --data '{ + "cardName": "", + "phone": "" +}' +``` + + diff --git a/v2/gp-onchain/v2-account-statement.mdx b/v2/gp-onchain/v2-account-statement.mdx new file mode 100644 index 0000000..869e76e --- /dev/null +++ b/v2/gp-onchain/v2-account-statement.mdx @@ -0,0 +1,32 @@ +--- +title: Account Statements +description: Retrieve account statements for users +--- + +# Retrieve Account Statement + +Account statement is a chronological record of all financial activity associated with a user's account. It provides a transparent view of money moving in and out of the account over a selected period of time, giving users complete visibility into their account activity. + +To retrieve the account statement, use the [`GET /user/statement`](/api-reference/user/get-account-statement) endpoint. + + + +```bash +curl --request GET \ + --url "https://gp-auth-module.sandbox.gnosispay.in/user/statement?limit=&cursor=&startDate=&endDate=" +``` + + +```bash +curl --request GET \ + --url "https://gp-auth-module.prod.gnosispay.com/user/statement?limit=&cursor=&startDate=&endDate=" +``` + + + +## Types of Account Statement +| Transaction Type | Possible Statuses | +|------------------|-------------------| +| **Card Transactions** | `pending`, `settled`, `cancelled`, `declined` | +| **Deposits** | `pending`, `approved`, `rejected` | +| **Withdrawals** | `pending`, `processing`, `completed`, `failed` | diff --git a/v2/gp-onchain/v2-account.mdx b/v2/gp-onchain/v2-account.mdx new file mode 100644 index 0000000..1da05de --- /dev/null +++ b/v2/gp-onchain/v2-account.mdx @@ -0,0 +1,100 @@ +--- +title: "GP Account" +description: "Understanding Gnosis Pay V2 account architecture and modules" +--- + +Gnosis Pay's on-chain infrastructure is built on top of the Safe protocol, providing a secure and programmable foundation for digital payments. Each user receives a unique Safe account that acts as their on-chain wallet, with additional modules that enable spending limits, time delays, and delegated spending capabilities. + +## Architecture Overview + +The Gnosis Pay Safe consists of a core Safe account with three main modules: + +- **Core Safe Account**: The base Safe wallet that holds funds and processes transactions +- **Delay Module**: Controls transaction timing and provides security delays +- **Roles Module**: Manages spending permissions and allowance limits + +## V2 Architecture Changes + +Gnosis Pay V2 maintains the same Safe wallet structure with delay and roles modules, but implements key behavioral improvements: + +### Instant Withdrawals +Withdrawals from the Gnosis Pay Safe reduce the spendable balance immediately to avoid double spending and then sync with the on-chain balance. During this process, the card is not blocked from operations. + +### Multi-Currency Support +The Roles module now supports multi-token configurations so that users can spend different currencies via their spending safe. + +## Module Configurations: + +### Delay Module Settings +- **Transaction delay**: 25 hours +- **Transaction expiration**: 31 hours + + +These extended timeouts are designed to discourage direct delay module interaction. Failed transactions require the full expiration period before they can be skipped. + + +### Daily Spending Limits + +The maximum daily card spend limit is **\$10K per token**. The Roles module enforces a hardcoded on-chain limit of **\$20K per token**, twice the card limit. This buffer exists because card clearing and settlement can take 1–4 days after a transaction, and the higher on-chain limit ensures uninterrupted card operations over that window without requiring any additional user signatures. + + + + Any interaction with the delay module will block the card and require customer support intervention to resolve the issue. + + + +## Account details for authenticated user +To get authenticated user account details, call [`GET /user/account`](/api-reference/user/get-account) + + + + ```bash + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/account + ``` + + + ```bash + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/user/account + ``` + + + +This will return configuration of user account including chain name, configured currencies, safe owner address etc. + +## Account balances for authenticated user + +Gnosis Pay V2 provides detailed balance breakdowns to give users complete visibility into their fund status. The authenticated user's account displays four distinct balance types: + +| Balance Type | Description | Increases When | Decreases When | +|--------------|-------------|----------------|----------------| +| **Spendable** | Amount available for immediate spending via card transactions or withdrawals | • Deposits clear AML analysis
• Refunds are processed
• Returns of goods are credited | • Card transactions are authorized
• Withdrawals are initiated | +| **Non-Spendable** | Funds that cannot be used for spending. Flagged for review or potential return | • Dependent on AML checks | • Funds are returned to source
• Funds are manually released after review | +| **Processing Deposits** | Incoming deposits currently undergoing AML analysis. Not yet available for spending | • New deposit is received | • AML analysis completes (moves to spendable or non-spendable) | +| **Processing Withdrawals** | Outgoing withdrawals currently being processed. Deducted from spendable but not yet settled on-chain | • Withdrawal is initiated | • Withdrawal settles on-chain | + +### Get Account Balances + + +To request the account balance of the user, call [`GET /user/balances/v2`](/api-reference/user/get-account-balances-v2) + + + +```bash +curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/balances/v2 +``` + + +```bash +curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/user/balances/v2 +``` + + + + + +Each currency maintains its own separate balance states. For example, USDC and USDT will each have their own spendable, non-spendable, processing deposits, and processing withdrawals amounts. + diff --git a/v2/gp-onchain/v2-withdraw.mdx b/v2/gp-onchain/v2-withdraw.mdx new file mode 100644 index 0000000..df8336d --- /dev/null +++ b/v2/gp-onchain/v2-withdraw.mdx @@ -0,0 +1,51 @@ +--- +title: On-chain Withdrawals +description: Create request to withdraw funds onchain to wallet address +--- +# Create Withdrawal Request + +The [`POST /user/withdrawals`](/api-reference/user/create-withdrawal) endpoint allows you to create a withdrawal request for a user. +The withdrawal happens to the GP Safe's Owner wallet address. + + +**Withdrawals are per token.** Each withdrawal request targets a single token via `tokenAddress`. + + + +**Withdrawal Restrictions:** +- Only specific tokens are currently supported for withdrawals +- Withdrawals are restricted to the Safe owner address — the wallet address that initiated the Safe deployment via the [POST /user/account](/api-reference/user/create-account) endpoint. + + + + Token addresses supported for withdrawals on Celo: + - CeloUSDT = "0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e" + - CeloUSDC = "0xceba9300f2b948710d2653dd7b07f33a8b32118c" + + + + + +```bash +curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/withdrawal \ + --header 'Content-Type: application/json' \ + --data '{ + "tokenAddress": "", + "amount": "" +}' +``` + + + +```bash +curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/user/withdrawal \ + --header 'Content-Type: application/json' \ + --data '{ + "tokenAddress": "", + "amount": "" +}' +``` + + diff --git a/v2/v1-v2-diff.mdx b/v2/v1-v2-diff.mdx new file mode 100644 index 0000000..5a4e099 --- /dev/null +++ b/v2/v1-v2-diff.mdx @@ -0,0 +1,17 @@ +--- +title: "GP V1 vs V2 Comparison" +description: "Overview of the differences between GP V1 and V2 infra" +--- + +| Feature | V1 | V2 | +|---------|----|----| +| **SIWE Authentication** | Long-lived access token (24h) | Access token (15 min validity) and refresh token model (7 days validity) with rotation support | +| **Refunds and Reversals** | Refunds and reversals processed after 1 business day | Refunds and Reversals instantly processed right after clearing message. | +| **KYC Verification** | Dependent on Gnosis Pay to complete KYC and gnosis pay shares KYC to partners | KYC can performed by Partners and shared with Gnosis Pay | +| **Safe Deployment** | Dedicated Safe deployment endpoint | Single `/account` endpoint handling provisioning + creation | +| **Token Management** | Limited support for single token currency| Multi-currency support with different balance states | +| **Account Balances** | Includes spendable, non-spendable and total balance. | Detailed breakdown of balances including spendable, authorized holds, non-spendable, processing deposits | +| **Withdrawals** | Withdrawals subject to delay module | Instant withdrawals bypassing delay module | +| **Daily Onchain Spending Limits** | Set by user, requires an onchain signature | Hardcoded at $20,000 daily limit (2x card limit buffer), no signatures required | +| **Delay Module Configuration** | 3-minute delay, 30-minute expiration | 25-hour delay, 31-hour expiration (discourages usage) | +| **Transactions** | Separated by card transaction and onchain transactions,onchain transactions only available via RPC | Unified account-statement endpoint showing deposits, withdrawals, and card transactions | diff --git a/v2/v2-onboard.mdx b/v2/v2-onboard.mdx new file mode 100644 index 0000000..5e5036b --- /dev/null +++ b/v2/v2-onboard.mdx @@ -0,0 +1,425 @@ +--- +title: "Onboard to Gnosis Pay V2" +description: "User onboarding using Gnosis Pay V2" +--- +This documentation provides a step-by-step guide on how to test the user onboarding experience for Gnosis Pay V2 in a sandbox environment. + +## 1. Authentication + + + +To get access token, please go through the [authentication guide here](/v2/v2-siwe-auth#authentication-flow-2). + + + + +## 2. User Registration + + + + Send an email verification request to [`POST /email-verification`](/api-reference/auth/request-email-verification) with a One-Time Password (OTP) to the user's email address. + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/email-verification \ + --header 'Content-Type: application/json' \ + --data '{ + "email": "jsmith@example.com" + }' + ``` + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/email-verification \ + --header 'Content-Type: application/json' \ + --data '{ + "email": "jsmith@example.com" + }' + ``` + + + + + + Register a new user with email OTP and create a user profile. POST [`/user`](/api-reference/user/register-user) endpoint returns a new access token with userID that must be used as the bearer token for all subsequent API requests - otherwise the calls will fail. + + Optionally, provide a shareToken for Sumsub reusable KYC. + + If the access token lacks a `userID`, it indicates that user registration failed because the email was never properly submitted. + + + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/user \ + --header 'Content-Type: application/json' \ + --data '{ + "email": "jsmith@example.com", + "otp": "", + "shareToken": "" + }' + ``` + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/user \ + --header 'Content-Type: application/json' \ + --data '{ + "email": "jsmith@example.com", + "otp": "", + "shareToken": "" + }' + ``` + + + + + + After user registration, use the access token to retrieve the user's current onboarding status.[`GET /user/onboarding`](/api-reference/user/get-onboarding-status) will indicate the next required step in the onboarding process. + + #### User Onboarding States + + The onboarding status values to indicate the current step and required user actions: + + | Status | Description | User Action Required | + |--------|-------------|---------------------| + | `action_accept_tos` | Terms of Service acceptance required - user registered but hasn't accepted required terms | Review and accept Terms of Service | + | `waiting_kyc_setup` | KYC initialization in progress - all terms accepted, KYC process being set up | Wait for KYC setup completion | + | `action_complete_kyc` | KYC verification pending - KYC check created but pending user completion | Complete KYC verification via Sumsub `webSdkUrl` | + | `kyc_manual_review` | KYC provider flagged the check as requiring manual review | This is a non-recoverable state from the user's side, requires internal review for updating application status | + | `action_kyc_resubmission_requested` | KYC provider has requested the user resubmit their KYC documents — a new Sumsub session URL is provided | Re-complete KYC verification via Sumsub using the provided `webSdkUrl` | + | `action_complete_sof` | Source of Funds questionnaire required - KYC approved, SOF answers needed | Answer Source of Funds questionnaire | + | `action_create_account` | Ready for account creation - KYC approved and SOF completed | Proceed with account creation | + | `waiting_account_setup` | Account provisioning in progress - account is being set up in the system | Wait for account setup completion | + | `completed` | Onboarding complete - account is active with cardholder ID | Start using the platform | + | `rejected` | Onboarding rejected - account is blocked, closed, or KYC verification failed | Contact support or retry if applicable | + + + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/onboarding + ``` + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/user/onboarding + ``` + + + + + + + At this point, the user is registered in the Gnosis Pay system and has an associated `userID`. Call the endpoint [`GET /user/onboarding`](/api-reference/user/get-onboarding-status). The response from JSON will be following returned with status of `action_accept_tos`. + ```json + { + "status": "action_accept_tos", + "terms": [ + { + "type": "string", + "currentVersion": "string", + "name": "string", + "url": null, + "accepted": true, + "acceptedVersion": null, + "acceptedAt": null + } + ] + } + ``` + + UX tip: Handle ToS acceptance within the email registration flow to avoid introducing an additional screen in the onboarding process. In the same screen, render the Terms (or link to them) and include a mandatory “I agree to the Terms of Service” checkbox. Disable form submission until the checkbox is selected. After successful registration and token issuance, automatically trigger the SoF acceptance call with the new token. + + #### Request Terms of Service + Fetch the current `Terms of Service` that users must agree to before continuing with the onboarding process via the endpoint [`GET /terms`](/api-reference/terms/list-available-terms). + + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/terms + ``` + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/terms + ``` + + + + Submit the accepted Terms of Service via the [`POST /user/terms`](/api-reference/terms/accept-terms) endpoint. + + + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/terms \ + --header 'Content-Type: application/json' \ + --data '{ + "terms": [ + { + "type": "", + "version": "" + } + ] + }' + ``` + + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/user/terms \ + --header 'Content-Type: application/json' \ + --data '{ + "terms": [ + { + "type": "", + "version": "" + } + ] + }' + ``` + + + + You can check the status of the user's acceptance of the Terms of Service via the [`GET /user/terms`](api-reference/terms/get-user-terms-status) endpoint. + + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/terms + + ``` + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/user/terms + + ``` + + + + + + +## 3. KYC Process + + + + Call the [`GET /user/onboarding`](/api-reference/user/get-onboarding-status) API to retrieve the current status of the user's onboarding process. If the status is `action_complete_kyc`, you can proceed with the KYC process. The response will include the status and a `webSdkURL` for completing KYC verification. + ```json + { + "status": "action_complete_kyc", + "webSdkUrl": "https://in.sumsub.com/websdk/p/sbx_yeuhsxyuhkio" + } + ``` + + + + + + + + Access the Sumsub URL provided in the response to begin the KYC verification process. In the sandbox environment, you can use Sumsub's predefined templates for testing purposes. + + + As you are in the sandbox environment, you can use fake documents or use one provided by Sumsub. + + + To complete the verification process: + 1. Open an iframe with the provided `webSdkUrl` + 2. Use the [verification document templates](https://docs.sumsub.com/docs/verification-document-templates) for Proof of Identity (POI) and Proof of Address (POA) + 3. Complete the liveness check as prompted + + + Access the Sumsub URL provided in the response to begin the KYC verification process. + + To complete the verification process: + 1. Open an iframe with the provided `webSdkUrl` + 2. Upload valid Proof of Identity (POI) and Proof of Address (POA) documents + 3. Complete the liveness check as prompted + + + + + + + + If [`GET /user/onboarding`](/api-reference/user/get-onboarding-status) returns `action_kyc_resubmission_requested`, the KYC provider has reviewed the submission and is requesting the user resubmit their documents. The response shape is identical to `action_complete_kyc` and includes a fresh `webSdkUrl`: + + ```json + { + "status": "action_kyc_resubmission_requested", + "webSdkUrl": "https://in.sumsub.com/websdk/p/sbx_yeuhsxyuhkio" + } + ``` + + Open the Sumsub iframe again with the provided `webSdkUrl` and have the user resubmit their documents, following the same steps as the initial KYC completion. + + + + + + If [`GET /user/onboarding`](/api-reference/user/get-onboarding-status) returns `kyc_manual_review`, the KYC provider has flagged the check for manual review. This is a non-recoverable state from the user's side — no `webSdkUrl` is provided and the user cannot self-resolve it. + + ```json + { + "status": "kyc_manual_review" + } + ``` + + + Display a clear message directing the user to contact the customer support. No further onboarding actions are available until the manual review is resolved. + + + + + + After completing the KYC process, the next steps is to proceed to the Source of Funds (SOF) verification step. Call the [`GET /user/onboarding`](/api-reference/user/get-onboarding-status), which will return the next required action to complete SOF verification along with the questions that need to be answered. Alternatively, retrieve the SOF questions directly using the [`GET / + source-of-funds`](/api-reference/source-of-funds/get-sof-questions) endpoint. + + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/source-of-funds + ``` + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/source-of-funds + ``` + + + + Once all SOF questions have been reviewed and answered by user, submit the responses through the [`POST /source-of-funds`](/api-reference/source-of-funds/submit-sof-answers) endpoint to complete this step of the onboarding process. + + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/source-of-funds \ + --header 'Content-Type: application/json' \ + --data '{ + "answers": [ + { + "question": "", + "answer": "" + } + ] + }' + ``` + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/source-of-funds \ + --header 'Content-Type: application/json' \ + --data '{ + "answers": [ + { + "question": "", + "answer": "" + } + ] + }' + ``` + + + + + + + +## 4. Account Setup + + + + Check the user's onboarding status using the [`GET /user/onboarding`](/api-reference/user/get-onboarding-status) endpoint to determine if their KYC verification has been accepted. If the KYC is approved, the user status will transition to the following: + ```json + { + "status": "action_create_account" + } + ``` + In next step, we will create GP spending safe wallet address. + + + + In this step, you will initiate the Gnosis Pay account provisioning and creation process. Call [`POST /user/account`](/api-reference/user/create-account) endpoint to create an account for the authenticated user. + + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/user/account \ + --header 'Content-Type: application/json' \ + --data '{ + "celoConfig": { + "dailyLimits": { + "usdt": 1, + "usdc": 1 + } + } + }' + ``` + + + ```bash cURL + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/user/account \ + --header 'Content-Type: application/json' \ + --data '{ + "celoConfig": { + "dailyLimits": { + "usdt": 1, + "usdc": 1 + } + } + }' + ``` + + + + Once the account provisioning begins, the onboarding status will be updated to `waiting_account_setup` state, indicating that the account is being set up in the system. + ```json + { + "status": "waiting_account_setup" + } + ``` + + + + + Call the [`GET /user`](/api-reference/user/get-current-user) endpoint to retrieve the full user profile, including their GP spending wallet address. + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/user \ + --header 'Content-Type: application/json' + ``` + + + ```bash cURL + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/user \ + --header 'Content-Type: application/json' + ``` + + + + + diff --git a/v2/v2-overview.mdx b/v2/v2-overview.mdx new file mode 100644 index 0000000..58f53f5 --- /dev/null +++ b/v2/v2-overview.mdx @@ -0,0 +1,66 @@ +--- +title: "Gnosis Pay V2 Overview" +description: "Welcome to Gnosis Pay V2 documentation" +--- + +Gnosis Pay V2 is **stablecoin card-as-a-service infrastructure** that enables businesses and product teams to launch and operate stablecoin card programs, including both virtual and physical cards. + +Partners can run their own branded card programs and fully own their user relationships, while our platform handles the complexity of card network integrations, settlement flows, and compliance. + +Designed with developers in mind, Gnosis Pay V2 provides APIs to build and scale card programs end-to-end. Developers get access to a dedicated sandbox environment where they can test Visa transactions through the fully integrated Pismo Simulator, as well as simulate workflows like KYC using sandbox integrations. + + +## What's New in Gnosis Pay V2 + + + + Deploy Gnosis Pay stack across blockchain networks-not limited to a single chain. + + + + Enable ecosystem's stablecoin as a spending currency, fully on-chain with native support. + + + + Instant withdrawals from spending account to owner wallet address via our API endpoint. + + + + Instant refunds upon receiving clearing messages. + + + + + EUR, USD, GBP accounts available by default with multi-currency support. + + + + + Available across Brazil, EU, UK, Cyprus, Colombia, Philippines, Mexico, Japan, Indonesia, Thailand, Singapore, and expanding + + + +--- + +## V2 Platform Capabilities + + + + + Complete testing environment with instant KYC approval via Sumsub integration and simulate card transaction via Pismo. + + + + Instant notifications for all account and card transaction events. + + + + Self-service dashboard with partner management capabilities and handle customer support requests. + + + + Token-based authentication with automatic rotation and session management. + + + + diff --git a/v2/v2-siwe-auth.mdx b/v2/v2-siwe-auth.mdx new file mode 100644 index 0000000..14f6ad7 --- /dev/null +++ b/v2/v2-siwe-auth.mdx @@ -0,0 +1,177 @@ +--- +title: "Authentication Guide" +description: "Guide for managing access authentication" +--- +As part of the Gnosis Pay V2 integration, we are introducing a more secure authentication mechanism. V2 authentication utilizes a token-based system with **access tokens** and **refresh tokens**, replacing the previous long-lived JWT approach. We now provide short-lived access tokens (15 minutes) paired with long-lived refresh tokens (7 days) for enhanced security. This guide will walk you through implementing the new authentication flow in your application. + +### Token Types + +| Property | Access Token | Refresh Token | +|----------|-------------|---------------| +| **Lifespan** | 15 minutes | 7 days | +| **Type** | Stateless JWT | Opaque token (secure random string) | +| **Purpose** | Used for API requests and contains user claims | Used to obtain new access tokens when they expire | +| **Storage** | Memory or short-term storage (not localStorage) | Secure, encrypted storage on the user’s device (must be handled securely by the application)| + + +### Authentication Flow + +```mermaid +sequenceDiagram + participant Client + participant API + participant DB + + Client->>API: 1. SIWE Login + API->>Client: 2. Access Token (15min) + Refresh Token (7d) + + Client->>API: 3. API Request with Access Token + API->>Client: 4. Success Response + + Note over Client: Access token expires + + Client->>API: 5. API Request (401 Unauthorized) + Client->>API: 6. Refresh Token Request + API->>DB: 7. Validate & Rotate Tokens + API->>Client: 8. New Access Token + New Refresh Token + + Client->>API: 9. Retry API Request + API->>Client: 10. Success Response +``` + +## Authentication Flow + +Let's take a look at how you can complete SIWE validation and retrieve access tokens. + + + + Request a SIWE message by calling the endpoint with the wallet address in the path and required query parameters. + + + **Domain Whitelisting Required**: We validate domains on our end for security. Your SIWE message must originate from a domain that has been pre-approved and whitelisted in our system. Contact our team to whitelist your domain before implementing authentication. + + + See full specification: [GET /auth/siwe/{address}](/api-reference/auth/get-siwe-message) + + + + + ```bash + curl --request GET \ + --url https://gp-auth-module.sandbox.gnosispay.in/auth/siwe/{address}?domain=&uri=&appName= + ``` + + + ```bash + curl --request GET \ + --url https://gp-auth-module.prod.gnosispay.com/auth/siwe/{address}?domain=&uri=&appName= + ``` + + + + + + Submit the signed SIWE message to verify authentication and receive your token pair. Send the wallet address, signature from the user's wallet, and the original SIWE message to get both access and refresh tokens. + + See full specification: [POST /auth/siwe](/api-reference/auth/get-access-token) + + + + ```bash + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/auth/siwe \ + --header 'Content-Type: application/json' \ + --data '{ + "address": "", + "signature": "", + "message": "" + }' + ``` + + + ```bash + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/auth/siwe \ + --header 'Content-Type: application/json' \ + --data '{ + "address": "", + "signature": "", + "message": "" + }' + ``` + + + + + **Secure Storage Required**: Store the refresh token securely to prevent XSS attacks. While our system has mechanisms to invalidate sessions if tokens are compromised, developers must implement proper security measures and secure storage APIs to protect against client-side vulnerabilities. + + + + + Exchange a valid refresh token for a new access token and rotated refresh token. This should be called automatically when your access token expires (every 15 minutes) or when you receive a 401 response. + + See full specification: [POST /auth/refresh](/api-reference/auth/refresh-access-token) + + + + ```bash + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/auth/refresh \ + --header 'Content-Type: application/json' \ + --data '{ + "refreshToken": "your_refresh_token_here" + }' + ``` + + + ```bash + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/auth/refresh \ + --header 'Content-Type: application/json' \ + --data '{ + "refreshToken": "your_refresh_token_here" + }' + ``` + + + + **Token Rotation**: Each refresh request invalidates the previous refresh token and issues a new one. Always store the new refresh token from the response for subsequent refresh requests. + + + The refresh token should only be used once. If a refresh token is used twice, the user will be automatically logged out. Preventing race conditions is essential to maintain session integrity. + + + + +### Revoking access tokens + + To securely log out a user, revoke their current session by invalidating all refresh tokens in the token family. + + + After successful logout, remove the refresh token from your client's secure storage to complete the logout process. + + + See full specification: [POST /auth/logout](/api-reference/auth/logout) + + + + ```bash + curl --request POST \ + --url https://gp-auth-module.sandbox.gnosispay.in/auth/logout \ + --header 'Content-Type: application/json' \ + --data '{ + "refreshToken": "your_refresh_token_here" + }' + ``` + + + ```bash + curl --request POST \ + --url https://gp-auth-module.prod.gnosispay.com/auth/logout \ + --header 'Content-Type: application/json' \ + --data '{ + "refreshToken": "your_refresh_token_here" + }' + ``` + + diff --git a/v2/webhooks/partner-webhook.png b/v2/webhooks/partner-webhook.png new file mode 100644 index 0000000..7b9e0df Binary files /dev/null and b/v2/webhooks/partner-webhook.png differ diff --git a/v2/webhooks/webhook-events.mdx b/v2/webhooks/webhook-events.mdx new file mode 100644 index 0000000..c557451 --- /dev/null +++ b/v2/webhooks/webhook-events.mdx @@ -0,0 +1,672 @@ +--- +title: "Webhook Events" +description: "Complete list of webhook events triggered in V2" +--- + +## Account Events + +### 1. Account.balance.changed + +Triggered when the account balance changes. + +#### Event Trigger Points + +| Flow | Reason Value | +| ----------------------------- | ------------------------------- | +| Deposit approved | `deposit.approved` | +| Deposit rejected | `deposit.rejected` | +| Withdrawal completed | `withdrawal.completed` | +| Instant withdrawal reconciled | `withdrawal.completed` | +| Card authorization | `card.authorization.{category}` | + + +```typescript account.balance.changed +{ + "id": "evt_abc123def456...", + "type": "account.balance.changed", + "createdAt": "2026-03-30T12:00:00.000000000Z", + "data": { + "accountId": "550e8400-e29b-41d4-a716-446655440000", + "reason": "deposit.approved", + "referenceId": "ref-123", + "balances": [ + { + "currency": "EUR", + "decimals": 18, + "spendable": "1000000000000000000", + "nonSpendable": "500000000000000000", + "processingDeposits": "200000000000000000", + "processingWithdrawals": "100000000000000000" + }, + { + "currency": "GBP", + "decimals": 18, + "spendable": "0", + "nonSpendable": "0", + "processingDeposits": "0", + "processingWithdrawals": "0" + }, + { + "currency": "USD", + "decimals": 6, + "spendable": "1000000", + "nonSpendable": "50000", + "processingDeposits": "100000", + "processingWithdrawals": "0" + } + ] + } + } +``` + + +### 2. Account.created + +Triggered when a new account is created and provisioning begins. + + +```typescript account.created +{ + "id": "evt_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", + "type": "account.created", + "createdAt": "2026-03-11T14:30:00.123456789Z", + "data": { + "id": "019505a1-b2c3-7d4e-a5f6-a1b2c3d4e5f6", + "kycProfileId": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "safeOwnerAddress": "0x1234567890abcdef1234567890abcdef12345678", + "safeAddress": null, + "chain": { + "name": "gnosis", + "id": "100" + }, + "reference": null, + "nativeCurrency": "EUR", + "additionalCurrencies": ["GBP"], + "createdAt": "2026-03-11T14:30:00.123456789Z", + "updatedAt": "2026-03-11T14:30:00.123456789Z", + "closedAt": null, + "status": "provisioning", + "provisionedAt": null + } +} +``` + + +### 3. Account.activated + +Triggered when account provisioning is complete and account becomes active. + + +```typescript account.activated +{ + "id": "evt_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", + "type": "account.activated", + "createdAt": "2026-03-11T15:00:00.123456789Z", + "data": { + "id": "019505a1-b2c3-7d4e-a5f6-a1b2c3d4e5f6", + "kycProfileId": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "safeOwnerAddress": "0x1234567890abcdef1234567890abcdef12345678", + "safeAddress": "0xabcdef1234567890abcdef1234567890abcdef12", + "chain": { + "name": "gnosis", + "id": "100" + }, + "reference": "partner-ref-123", + "nativeCurrency": "EUR", + "additionalCurrencies": ["GBP"], + "createdAt": "2026-03-11T14:30:00.123456789Z", + "updatedAt": "2026-03-11T15:00:00.123456789Z", + "closedAt": null, + "status": "active", + "provisionedAt": "2026-03-11T14:45:00.123456789Z" + } +} +``` + + +### 4. Account.blocked + +Triggered when an account is blocked due to security or compliance reasons. + + +```typescript account.blocked +{ + "id": "evt_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", + "type": "account.blocked", + "createdAt": "2026-03-11T16:00:00.123456789Z", + "data": { + "id": "019505a1-b2c3-7d4e-a5f6-a1b2c3d4e5f6", + "kycProfileId": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "safeOwnerAddress": "0x1234567890abcdef1234567890abcdef12345678", + "safeAddress": "0xabcdef1234567890abcdef1234567890abcdef12", + "chain": { + "name": "gnosis", + "id": "100" + }, + "reference": "partner-ref-123", + "nativeCurrency": "EUR", + "additionalCurrencies": ["GBP"], + "createdAt": "2026-03-11T14:30:00.123456789Z", + "updatedAt": "2026-03-11T16:00:00.123456789Z", + "closedAt": null, + "status": "blocked", + "provisionedAt": "2026-03-11T14:45:00.123456789Z" + } +} +``` + + +## KYC Events + +### 5. KYC.profile.approved + +Triggered when a KYC profile verification is approved. + + +```typescript kyc.profile.approved +{ + "id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", + "type": "kyc.profile.approved", + "createdAt": "2026-03-11T14:00:00.123456789Z", + "data": { + "id": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "reference": "applicant-abc123", + "email": "user@example.com", + "status": "approved", + "tenantId": "tenant-xyz", + "details": { + "fullName": "John Doe", + "phoneNumber": "+441234567890", + "dateOfBirth": "1990-01-15T00:00:00Z", + "nationality": "GB", + "countryOfResidence": "GB", + "countryOfBirth": "GB", + "documentNumber": "AB123456C", + "gender": "male", + "addresses": [ + { + "id": "019505a1-e5f6-7a7b-c8d9-e0f1a2b3c4d5", + "country": "GB", + "postalCode": "SW1A 1AA", + "city": "London", + "state": "England", + "address1": "10 Downing Street", + "address2": null + } + ] + } + } +} +``` + + +### 6. KYC.profile.rejected + +Triggered when a KYC profile verification is rejected. + + +```typescript kyc.profile.rejected +{ + "id": "evt_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", + "type": "kyc.profile.rejected", + "createdAt": "2026-03-11T14:00:00.123456789Z", + "data": { + "id": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "reference": "applicant-abc123", + "email": "user@example.com", + "status": "rejected", + "tenantId": "tenant-xyz", + "details": { + "fullName": "John Doe", + "phoneNumber": "+441234567890", + "dateOfBirth": "1990-01-15T00:00:00Z", + "nationality": "GB", + "countryOfResidence": "GB", + "countryOfBirth": "GB", + "documentNumber": "AB123456C", + "gender": "male", + "addresses": [ + { + "id": "019505a1-e5f6-7a7b-c8d9-e0f1a2b3c4d5", + "country": "GB", + "postalCode": "SW1A 1AA", + "city": "London", + "state": "England", + "address1": "10 Downing Street", + "address2": null + } + ] + } + } +} +``` + + +### 7. KYC.profile.requires_action + +Triggered when a KYC profile requires additional action from the user. + + +```typescript kyc.profile.requires_action +{ + "id": "evt_f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1", + "type": "kyc.profile.requires_action", + "createdAt": "2026-03-11T14:00:00.123456789Z", + "data": { + "id": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "reference": "applicant-abc123", + "email": "user@example.com", + "status": "requires_action", + "tenantId": "tenant-xyz", + "details": null + } +} +``` + + +### 8. KYC.profile.resubmission_requested + +Triggered when KYC profile requires document resubmission. + + +```typescript kyc.profile.resubmission_requested +{ + "id": "evt_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", + "type": "kyc.profile.resubmission_requested", + "createdAt": "2026-03-11T14:00:00.123456789Z", + "data": { + "id": "019505a1-c3d4-7e5f-b6a7-b2c3d4e5f6a7", + "reference": "applicant-abc123", + "email": "user@example.com", + "status": "resubmission_requested", + "tenantId": "tenant-xyz", + "details": null + } +} +``` + + +### 9. KYC.terms.approved + +Triggered when all required terms are accepted. + + +```typescript kyc.terms.approved +{ + "id": "evt_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", + "type": "kyc.terms.approved", + "createdAt": "2026-03-17T10:30:00.000000000Z", + "data": { + "profileId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "checkType": "terms", + "status": "approved", + "reference": "partner-user-ref-123" + } +} +``` + + +### 10. KYC.source_of_funds.approved + +Triggered when source of funds questionnaire is submitted and approved. + + +```typescript kyc.source_of_funds.approved +{ + "id": "evt_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", + "type": "kyc.source_of_funds.approved", + "createdAt": "2026-03-17T10:35:00.000000000Z", + "data": { + "profileId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "checkType": "source_of_funds", + "status": "approved", + "reference": "partner-user-ref-123" + } +} +``` + + +## Deposit Events + +### 11. Deposit.pending + +Triggered when an on-chain deposit is detected and awaiting KYT processing. + + +```typescript deposit.pending +{ + "id": "evt_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", + "type": "deposit.pending", + "createdAt": "2026-03-17T11:00:00.000000000Z", + "data": { + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "status": "pending", + "amount": "1000000", + "currency": "USDCe", + "decimals": 6, + "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "chain": { + "name": "gnosis", + "id": "100" + }, + "createdAt": "2026-03-17T10:59:30.000000000Z" + } +} +``` + + +### 12. Deposit.approved + +Triggered when a deposit is confirmed after KYT and funds become available. + + +```typescript deposit.approved +{ + "id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", + "type": "deposit.approved", + "createdAt": "2026-03-17T11:00:00.000000000Z", + "data": { + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "status": "approved", + "amount": "1000000", + "currency": "USDCe", + "decimals": 6, + "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "chain": { + "name": "gnosis", + "id": "100" + }, + "createdAt": "2026-03-17T10:59:30.000000000Z" + } +} +``` + + +### 13. Deposit.rejected + +Triggered when a deposit is rejected by KYT. + + +```typescript deposit.rejected +{ + "id": "evt_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", + "type": "deposit.rejected", + "createdAt": "2026-03-17T11:00:00.000000000Z", + "data": { + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "status": "rejected", + "amount": "1000000", + "currency": "USDCe", + "decimals": 6, + "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "chain": { + "name": "gnosis", + "id": "100" + }, + "createdAt": "2026-03-17T10:59:30.000000000Z" + } +} +``` + + +## Withdrawal Events + +### 14. Withdrawal.completed + +Triggered when a withdrawal is successfully processed on-chain. + + +```typescript withdrawal.completed +{ + "id": "evt_f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1", + "type": "withdrawal.completed", + "createdAt": "2026-03-17T12:15:00.000000000Z", + "data": { + "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "status": "completed", + "amount": "500000", + "currency": "USDCe", + "decimals": 6, + "transactionHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", + "chain": { + "name": "gnosis", + "id": "100" + }, + "createdAt": "2026-03-17T12:10:00.000000000Z" + } +} +``` + + +### 15. Withdrawal.failed + +Triggered when a withdrawal fails at any stage. + + +```typescript withdrawal.failed +{ + "id": "evt_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", + "type": "withdrawal.failed", + "createdAt": "2026-03-17T12:15:00.000000000Z", + "data": { + "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "status": "failed", + "amount": "500000", + "currency": "USDCe", + "decimals": 6, + "transactionHash": "", + "chain": { + "name": "gnosis", + "id": "100" + }, + "createdAt": "2026-03-17T12:10:00.000000000Z" + } +} +``` + + +## Card Events + +### 16. Card.created + +Triggered when a virtual card is provisioned or physical card reaches pending activation. + + +```typescript card.created +{ + "id": "evt_b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", + "type": "card.created", + "createdAt": "2026-03-17T14:00:00.000000000Z", + "data": { + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "type": "virtual", + "status": "created", + "last4": "4321", + "createdAt": "2026-03-17T14:00:00.000000000Z" + } +} +``` + + +### 17. Card.activated + +Triggered when a physical card is activated. + + +```typescript card.activated +{ + "id": "evt_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", + "type": "card.activated", + "createdAt": "2026-03-17T15:00:00.000000000Z", + "data": { + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "type": "plastic", + "status": "activated", + "last4": "4321", + "createdAt": "2026-03-17T14:00:00.000000000Z" + } +} +``` + + +### 18. Card.blocked + +Triggered when a card is temporarily blocked. + + +```typescript card.blocked +{ + "id": "evt_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", + "type": "card.blocked", + "createdAt": "2026-03-17T15:30:00.000000000Z", + "data": { + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "type": "virtual", + "status": "blocked", + "last4": "4321", + "createdAt": "2026-03-17T14:00:00.000000000Z" + } +} +``` + + +### 19. Card.unblocked + +Triggered when a card is unblocked and restored to active status. + + +```typescript card.unblocked +{ + "id": "evt_e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6", + "type": "card.unblocked", + "createdAt": "2026-03-17T16:00:00.000000000Z", + "data": { + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "type": "virtual", + "status": "unblocked", + "last4": "4321", + "createdAt": "2026-03-17T14:00:00.000000000Z" + } +} +``` + + +### 20. Card.canceled + +Triggered when a card is permanently canceled. + + +```typescript card.canceled +{ + "id": "evt_f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7", + "type": "card.canceled", + "createdAt": "2026-03-17T16:30:00.000000000Z", + "data": { + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "accountId": "d4e5f6a7-b8c9-0123-4567-890abcdef012", + "type": "virtual", + "status": "canceled", + "last4": "4321", + "createdAt": "2026-03-17T14:00:00.000000000Z" + } +} +``` + + +## Card Transaction Events + +The following table shows all card transaction event types and when they are triggered: + +| Event Type | When It Fires | Pismo Category | +| ------------------------------------------------ | ---------------------------------------------------- | ----------------------------------------------------------- | +| `card.transaction.created` | New debit authorization (purchase, withdrawal, etc.) | AUTHORIZATION (debit processing codes) | +| `card.transaction.refund` | Credit voucher authorization (refund to cardholder) | AUTHORIZATION (credit processing codes: 20, 203100, PSM007) | +| `card.transaction.declined` | Authorization denied by Pismo | DECLINED | +| `card.transaction.cleared` | Transaction settled/cleared (B2 confirmation) | CONFIRMATION, AIRPORT_TAX, INSTALLMENT | +| `card.transaction.reversed` | Full cancellation of an authorization | CANCELLATION | +| `card.transaction.replacement` | Amount adjustment on a pending authorization | REPLACEMENT | +| `card.transaction.incremental` | Additional authorization amount added | INCREMENTAL | +| `card.transaction.partial_cancellation` | Partial reversal of an authorization | PARTIAL_CANCELLATION | +| `card.transaction.cancellation_reversal` | Undo of a previous cancellation | CANCELLATION_REVERSAL | +| `card.transaction.reversal_partial_cancellation` | Undo of a previous partial cancellation | REVERSAL_PARTIAL_CANCELLATION | + +### 21. Card.transaction.created + +Triggered when a new debit authorization occurs (purchase, withdrawal, etc.). + + +```typescript card.transaction.created +{ + "id": "evt_8a3c1d4f2b6e9870c2a5b4d6e8f0a1c3", + "type": "card.transaction.declined", + "createdAt": "2026-04-24T11:17:44.082000000Z", + "data": { + "id": "f1c84a96-3d05-4b6b-9f1a-b2e7c5d8a012", + "accountId": "12c4a7e2-7a91-4f4f-9e76-1c9e1c2a2b18", + "cardId": "8a3b2c91-4d7e-49ab-bf25-ef019a4e6b20", + "status": "declined", + "amount": "20", + "currency": "EUR", + "decimals": 2, + "originalAmount": "20", + "originalCurrency": "USD", + "originalDecimals": 2, + "isCredit": false, + "createdAt": "2026-04-24T11:17:43.000000000Z", + "updatedAt": "2026-04-24T11:17:43.000000000Z", + "description": "Kindle Svcs 888-802-3080 US", + "authorizationId": 717, + "authorizationCode": "XZ0COI", + "merchant": { + "name": "Kindle Svcs", + "city": "888-802-3080", + "country": "US", + "categoryCode": "5818" + }, + "entryMode": "0100", + "country": null, + "billing": { + "billingAmount": "20", + "billingCurrency": { + "symbol": "€", + "code": "EUR", + "decimals": 2, + "name": "Euro" + } + }, + "transactionCurrency": { + "symbol": "$", + "code": "USD", + "decimals": 2, + "name": "US Dollar" + }, + "cardToken": "8a3b2c91-4d7e-49ab-bf25-ef019a4e6b20", + "wallet": { + "provider": "apple_pay", + "deviceName": "aVBhZCAoMyk.", + "deviceType": "TABLET", + "deviceBrand": null, + "deviceModel": null + }, + "clearedAt": null, + "isPending": false, + "kind": "payment", + "transactionType": "0100", + "transactions": [], + "declineReason": { + "code": "810", + "message": "Insufficient balance" + } + } +} +``` + diff --git a/v2/webhooks/webhook.mdx b/v2/webhooks/webhook.mdx new file mode 100644 index 0000000..819c5fc --- /dev/null +++ b/v2/webhooks/webhook.mdx @@ -0,0 +1,90 @@ +--- +title: "Introduction to Webhooks" +description: "Step-by-step guide to set up and implement webhooks in your application with Partner Dashboard" +--- + +Webhooks provide real-time notifications when events happen in the Gnosis Pay system. Rather than constantly polling our APIs for updates, you can configure webhook endpoints to receive instant notifications about card transactions, account status and balances, kyc statuses, and more. + + +## Enable Webhooks + +To implement webhooks in your application, follow these essential steps: + + + +Create a publicly accessible HTTP endpoint in your application that can receive POST requests. This endpoint must be available over HTTPS and return a 2xx status code to acknowledge receipt of webhook events. + + +Your webhook endpoint must be publicly accessible. For local development, use tools like [ngrok](https://ngrok.com/) to expose your local server. + + + + +Configure your webhook endpoint URL directly through the **Partners Dashboard**. Provide the complete HTTPS URL where you want to receive webhook notifications. +![Partner Dashboard](/v2/webhooks/partner-webhook.png) + + + ``` + https://partner-dashboard.sandbox.gnosispay.com/ + ``` + + + + + + +When events happen in the Gnosis Pay system, we'll send HTTP POST requests to your webhook endpoint with event data and cryptographic signatures. + +All webhooks include cryptographic signatures using **Ed25519 asymmetric cryptography**: + +- **`X-Webhook-Timestamp`**: Unix timestamp when the webhook was sent +- **`X-Webhook-Signature`**: Base64-encoded Ed25519 signature + + + **Always verify webhook signatures** before processing events. This ensures + the webhook originated from Gnosis Pay and hasn't been tampered with. + + + + + + +Extract the `Type` and `data` fields from the webhook payload. The `Type` identifies what happened (e.g., `user.created`, `kyc.status.changed`), while `data` contains the complete entity information. + +```json +{ + "id": "evt_a1b2c3d4e5f6...", + "type": "account.balance.changed", + "createdAt": "2026-03-04T12:00:00.000Z", + "data": { + "accountId": "550e8400-e29b-41d4-a716-446655440000", + "balances": [ + ] + .... + } +} +``` + +Handle each event type appropriately in your application. Since we send complete entity data, you typically won't need additional API calls to get the full context. + + +Process events idempotently to handle potential duplicates, and implement proper error handling and logging for monitoring. + + + +**Retry Policy**: +- **Max attempts**: 5 retries +- **Timeout**: 30 seconds per request +- **4xx responses**: Treated as permanent failures (no retry) - endpoint misconfiguration +- **5xx/connection errors**: Retried up to maximum attempts +- **Config changes**: Jobs cancelled if webhook config is paused/deleted during delivery + +If your webhook endpoint returns a non-2xx status code, we'll retry delivery according to these rules. + + + + **Timeout**: Your webhook endpoint must respond within 30 seconds. Requests that exceed this timeout are considered failed and will trigger our retry mechanism. + + + + diff --git a/webhooks/events.mdx b/webhooks/events.mdx deleted file mode 100644 index 0ceec89..0000000 --- a/webhooks/events.mdx +++ /dev/null @@ -1,12270 +0,0 @@ -## KYC Events - -### kyc.phone-validation.changed - -Triggered when a user's phone validation status changes. - -**Event Type**: `kyc.phone-validation.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "kyc.phone-validation.changed"; - data: { - /** Unique user identifier */ - id: string; - /** User email address */ - email: string; - /** User phone number */ - phone: string; - /** User first name */ - firstName: string; - /** User last name */ - lastName: string; - /** Primary address line */ - address1: string; - /** Secondary address line */ - address2: string; - /** City */ - city: string; - /** Postal/ZIP code */ - postalCode: string; - /** State/Province */ - state: string; - /** Country */ - country: string; - /** Nationality country */ - nationalityCountry: string; - /** User's sign-in wallet addresses */ - signInWallets: { - /** Wallet address */ - address: string; - }[]; - /** User's safe wallet addresses */ - safeWallets: { - /** Safe wallet address */ - address: string; - /** Blockchain chain ID */ - chainId: string; - }[]; - /** KYC verification status */ - kycStatus: "notStarted" | "documentsRequested" | "pending" | "processing" | "approved" | "resubmissionRequested" | "rejected" | "requiresAction"; - /** Available features for the user */ - availableFeatures: { - /** Whether Monerium IBAN is available */ - moneriumIban: boolean; - }; - /** User's cards */ - cards: { - /** Card ID */ - id: string; - /** Card token */ - cardToken: string; - /** Last four digits of card */ - lastFourDigits: string; - /** Name embossed on card */ - embossedName: string; - /** Card activation timestamp */ - activatedAt: string; - /** Whether card is virtual */ - virtual: boolean; - }[]; - /** User's banking details */ - bankingDetails: { - /** Monerium IBAN */ - moneriumIban: string; - /** Monerium BIC */ - moneriumBic: string; - /** Monerium IBAN status */ - moneriumIbanStatus: string; - /** Banking address */ - address: string; - }; - /** Whether source of funds question was answered */ - isSourceOfFundsAnswered: boolean; - /** Whether phone number is validated */ - isPhoneValidated: boolean; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "kyc.phone-validation.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when a user's phone validation status changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "kyc.phone-validation.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique user identifier" - }, - "email": { - "type": "string", - "nullable": true, - "description": "User email address" - }, - "phone": { - "type": "string", - "nullable": true, - "description": "User phone number" - }, - "firstName": { - "type": "string", - "nullable": true, - "description": "User first name" - }, - "lastName": { - "type": "string", - "nullable": true, - "description": "User last name" - }, - "address1": { - "type": "string", - "nullable": true, - "description": "Primary address line" - }, - "address2": { - "type": "string", - "nullable": true, - "description": "Secondary address line" - }, - "city": { - "type": "string", - "nullable": true, - "description": "City" - }, - "postalCode": { - "type": "string", - "nullable": true, - "description": "Postal/ZIP code" - }, - "state": { - "type": "string", - "nullable": true, - "description": "State/Province" - }, - "country": { - "type": "string", - "nullable": true, - "description": "Country" - }, - "nationalityCountry": { - "type": "string", - "nullable": true, - "description": "Nationality country" - }, - "signInWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Wallet address" - } - }, - "required": [ - "address" - ] - }, - "description": "User's sign-in wallet addresses" - }, - "safeWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Safe wallet address" - }, - "chainId": { - "type": "string", - "description": "Blockchain chain ID" - } - }, - "required": [ - "address", - "chainId" - ] - }, - "description": "User's safe wallet addresses" - }, - "kycStatus": { - "type": "string", - "enum": [ - "notStarted", - "documentsRequested", - "pending", - "processing", - "approved", - "resubmissionRequested", - "rejected", - "requiresAction" - ], - "description": "KYC verification status", - "example": "approved" - }, - "availableFeatures": { - "type": "object", - "properties": { - "moneriumIban": { - "type": "boolean", - "description": "Whether Monerium IBAN is available" - } - }, - "required": [ - "moneriumIban" - ], - "description": "Available features for the user" - }, - "cards": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Card ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "lastFourDigits": { - "type": "string", - "description": "Last four digits of card" - }, - "embossedName": { - "type": "string", - "nullable": true, - "description": "Name embossed on card" - }, - "activatedAt": { - "type": "string", - "nullable": true, - "description": "Card activation timestamp" - }, - "virtual": { - "type": "boolean", - "description": "Whether card is virtual" - } - }, - "required": [ - "id", - "cardToken", - "lastFourDigits", - "embossedName", - "activatedAt", - "virtual" - ] - }, - "description": "User's cards" - }, - "bankingDetails": { - "type": "object", - "nullable": true, - "properties": { - "moneriumIban": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN" - }, - "moneriumBic": { - "type": "string", - "nullable": true, - "description": "Monerium BIC" - }, - "moneriumIbanStatus": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN status" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Banking address" - } - }, - "required": [ - "moneriumIban", - "moneriumBic", - "moneriumIbanStatus", - "address" - ], - "description": "User's banking details" - }, - "isSourceOfFundsAnswered": { - "type": "boolean", - "description": "Whether source of funds question was answered" - }, - "isPhoneValidated": { - "type": "boolean", - "description": "Whether phone number is validated" - } - }, - "required": [ - "id", - "email", - "phone", - "firstName", - "lastName", - "address1", - "address2", - "city", - "postalCode", - "state", - "country", - "nationalityCountry", - "signInWallets", - "safeWallets", - "kycStatus", - "availableFeatures", - "cards", - "bankingDetails", - "isSourceOfFundsAnswered", - "isPhoneValidated" - ], - "description": "User data structure", - "example": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a user's phone validation status changes.", - "example": { - "eventType": "kyc.phone-validation.changed", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "kyc.phone-validation.changed", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } -} -``` - -### kyc.source-of-funds.changed - -Triggered when a user's source of funds information changes. - -**Event Type**: `kyc.source-of-funds.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "kyc.source-of-funds.changed"; - data: { - /** Unique user identifier */ - id: string; - /** User email address */ - email: string; - /** User phone number */ - phone: string; - /** User first name */ - firstName: string; - /** User last name */ - lastName: string; - /** Primary address line */ - address1: string; - /** Secondary address line */ - address2: string; - /** City */ - city: string; - /** Postal/ZIP code */ - postalCode: string; - /** State/Province */ - state: string; - /** Country */ - country: string; - /** Nationality country */ - nationalityCountry: string; - /** User's sign-in wallet addresses */ - signInWallets: { - /** Wallet address */ - address: string; - }[]; - /** User's safe wallet addresses */ - safeWallets: { - /** Safe wallet address */ - address: string; - /** Blockchain chain ID */ - chainId: string; - }[]; - /** KYC verification status */ - kycStatus: "notStarted" | "documentsRequested" | "pending" | "processing" | "approved" | "resubmissionRequested" | "rejected" | "requiresAction"; - /** Available features for the user */ - availableFeatures: { - /** Whether Monerium IBAN is available */ - moneriumIban: boolean; - }; - /** User's cards */ - cards: { - /** Card ID */ - id: string; - /** Card token */ - cardToken: string; - /** Last four digits of card */ - lastFourDigits: string; - /** Name embossed on card */ - embossedName: string; - /** Card activation timestamp */ - activatedAt: string; - /** Whether card is virtual */ - virtual: boolean; - }[]; - /** User's banking details */ - bankingDetails: { - /** Monerium IBAN */ - moneriumIban: string; - /** Monerium BIC */ - moneriumBic: string; - /** Monerium IBAN status */ - moneriumIbanStatus: string; - /** Banking address */ - address: string; - }; - /** Whether source of funds question was answered */ - isSourceOfFundsAnswered: boolean; - /** Whether phone number is validated */ - isPhoneValidated: boolean; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "kyc.source-of-funds.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when a user's source of funds information changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "kyc.source-of-funds.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique user identifier" - }, - "email": { - "type": "string", - "nullable": true, - "description": "User email address" - }, - "phone": { - "type": "string", - "nullable": true, - "description": "User phone number" - }, - "firstName": { - "type": "string", - "nullable": true, - "description": "User first name" - }, - "lastName": { - "type": "string", - "nullable": true, - "description": "User last name" - }, - "address1": { - "type": "string", - "nullable": true, - "description": "Primary address line" - }, - "address2": { - "type": "string", - "nullable": true, - "description": "Secondary address line" - }, - "city": { - "type": "string", - "nullable": true, - "description": "City" - }, - "postalCode": { - "type": "string", - "nullable": true, - "description": "Postal/ZIP code" - }, - "state": { - "type": "string", - "nullable": true, - "description": "State/Province" - }, - "country": { - "type": "string", - "nullable": true, - "description": "Country" - }, - "nationalityCountry": { - "type": "string", - "nullable": true, - "description": "Nationality country" - }, - "signInWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Wallet address" - } - }, - "required": [ - "address" - ] - }, - "description": "User's sign-in wallet addresses" - }, - "safeWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Safe wallet address" - }, - "chainId": { - "type": "string", - "description": "Blockchain chain ID" - } - }, - "required": [ - "address", - "chainId" - ] - }, - "description": "User's safe wallet addresses" - }, - "kycStatus": { - "type": "string", - "enum": [ - "notStarted", - "documentsRequested", - "pending", - "processing", - "approved", - "resubmissionRequested", - "rejected", - "requiresAction" - ], - "description": "KYC verification status", - "example": "approved" - }, - "availableFeatures": { - "type": "object", - "properties": { - "moneriumIban": { - "type": "boolean", - "description": "Whether Monerium IBAN is available" - } - }, - "required": [ - "moneriumIban" - ], - "description": "Available features for the user" - }, - "cards": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Card ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "lastFourDigits": { - "type": "string", - "description": "Last four digits of card" - }, - "embossedName": { - "type": "string", - "nullable": true, - "description": "Name embossed on card" - }, - "activatedAt": { - "type": "string", - "nullable": true, - "description": "Card activation timestamp" - }, - "virtual": { - "type": "boolean", - "description": "Whether card is virtual" - } - }, - "required": [ - "id", - "cardToken", - "lastFourDigits", - "embossedName", - "activatedAt", - "virtual" - ] - }, - "description": "User's cards" - }, - "bankingDetails": { - "type": "object", - "nullable": true, - "properties": { - "moneriumIban": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN" - }, - "moneriumBic": { - "type": "string", - "nullable": true, - "description": "Monerium BIC" - }, - "moneriumIbanStatus": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN status" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Banking address" - } - }, - "required": [ - "moneriumIban", - "moneriumBic", - "moneriumIbanStatus", - "address" - ], - "description": "User's banking details" - }, - "isSourceOfFundsAnswered": { - "type": "boolean", - "description": "Whether source of funds question was answered" - }, - "isPhoneValidated": { - "type": "boolean", - "description": "Whether phone number is validated" - } - }, - "required": [ - "id", - "email", - "phone", - "firstName", - "lastName", - "address1", - "address2", - "city", - "postalCode", - "state", - "country", - "nationalityCountry", - "signInWallets", - "safeWallets", - "kycStatus", - "availableFeatures", - "cards", - "bankingDetails", - "isSourceOfFundsAnswered", - "isPhoneValidated" - ], - "description": "User data structure", - "example": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a user's source of funds information changes.", - "example": { - "eventType": "kyc.source-of-funds.changed", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "kyc.source-of-funds.changed", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } -} -``` - -### kyc.status.changed - -Triggered when a user's KYC status changes. - -**Event Type**: `kyc.status.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "kyc.status.changed"; - data: { - /** Unique user identifier */ - id: string; - /** User email address */ - email: string; - /** User phone number */ - phone: string; - /** User first name */ - firstName: string; - /** User last name */ - lastName: string; - /** Primary address line */ - address1: string; - /** Secondary address line */ - address2: string; - /** City */ - city: string; - /** Postal/ZIP code */ - postalCode: string; - /** State/Province */ - state: string; - /** Country */ - country: string; - /** Nationality country */ - nationalityCountry: string; - /** User's sign-in wallet addresses */ - signInWallets: { - /** Wallet address */ - address: string; - }[]; - /** User's safe wallet addresses */ - safeWallets: { - /** Safe wallet address */ - address: string; - /** Blockchain chain ID */ - chainId: string; - }[]; - /** KYC verification status */ - kycStatus: "notStarted" | "documentsRequested" | "pending" | "processing" | "approved" | "resubmissionRequested" | "rejected" | "requiresAction"; - /** Available features for the user */ - availableFeatures: { - /** Whether Monerium IBAN is available */ - moneriumIban: boolean; - }; - /** User's cards */ - cards: { - /** Card ID */ - id: string; - /** Card token */ - cardToken: string; - /** Last four digits of card */ - lastFourDigits: string; - /** Name embossed on card */ - embossedName: string; - /** Card activation timestamp */ - activatedAt: string; - /** Whether card is virtual */ - virtual: boolean; - }[]; - /** User's banking details */ - bankingDetails: { - /** Monerium IBAN */ - moneriumIban: string; - /** Monerium BIC */ - moneriumBic: string; - /** Monerium IBAN status */ - moneriumIbanStatus: string; - /** Banking address */ - address: string; - }; - /** Whether source of funds question was answered */ - isSourceOfFundsAnswered: boolean; - /** Whether phone number is validated */ - isPhoneValidated: boolean; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "kyc.status.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when a user's KYC status changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "kyc.status.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique user identifier" - }, - "email": { - "type": "string", - "nullable": true, - "description": "User email address" - }, - "phone": { - "type": "string", - "nullable": true, - "description": "User phone number" - }, - "firstName": { - "type": "string", - "nullable": true, - "description": "User first name" - }, - "lastName": { - "type": "string", - "nullable": true, - "description": "User last name" - }, - "address1": { - "type": "string", - "nullable": true, - "description": "Primary address line" - }, - "address2": { - "type": "string", - "nullable": true, - "description": "Secondary address line" - }, - "city": { - "type": "string", - "nullable": true, - "description": "City" - }, - "postalCode": { - "type": "string", - "nullable": true, - "description": "Postal/ZIP code" - }, - "state": { - "type": "string", - "nullable": true, - "description": "State/Province" - }, - "country": { - "type": "string", - "nullable": true, - "description": "Country" - }, - "nationalityCountry": { - "type": "string", - "nullable": true, - "description": "Nationality country" - }, - "signInWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Wallet address" - } - }, - "required": [ - "address" - ] - }, - "description": "User's sign-in wallet addresses" - }, - "safeWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Safe wallet address" - }, - "chainId": { - "type": "string", - "description": "Blockchain chain ID" - } - }, - "required": [ - "address", - "chainId" - ] - }, - "description": "User's safe wallet addresses" - }, - "kycStatus": { - "type": "string", - "enum": [ - "notStarted", - "documentsRequested", - "pending", - "processing", - "approved", - "resubmissionRequested", - "rejected", - "requiresAction" - ], - "description": "KYC verification status", - "example": "approved" - }, - "availableFeatures": { - "type": "object", - "properties": { - "moneriumIban": { - "type": "boolean", - "description": "Whether Monerium IBAN is available" - } - }, - "required": [ - "moneriumIban" - ], - "description": "Available features for the user" - }, - "cards": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Card ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "lastFourDigits": { - "type": "string", - "description": "Last four digits of card" - }, - "embossedName": { - "type": "string", - "nullable": true, - "description": "Name embossed on card" - }, - "activatedAt": { - "type": "string", - "nullable": true, - "description": "Card activation timestamp" - }, - "virtual": { - "type": "boolean", - "description": "Whether card is virtual" - } - }, - "required": [ - "id", - "cardToken", - "lastFourDigits", - "embossedName", - "activatedAt", - "virtual" - ] - }, - "description": "User's cards" - }, - "bankingDetails": { - "type": "object", - "nullable": true, - "properties": { - "moneriumIban": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN" - }, - "moneriumBic": { - "type": "string", - "nullable": true, - "description": "Monerium BIC" - }, - "moneriumIbanStatus": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN status" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Banking address" - } - }, - "required": [ - "moneriumIban", - "moneriumBic", - "moneriumIbanStatus", - "address" - ], - "description": "User's banking details" - }, - "isSourceOfFundsAnswered": { - "type": "boolean", - "description": "Whether source of funds question was answered" - }, - "isPhoneValidated": { - "type": "boolean", - "description": "Whether phone number is validated" - } - }, - "required": [ - "id", - "email", - "phone", - "firstName", - "lastName", - "address1", - "address2", - "city", - "postalCode", - "state", - "country", - "nationalityCountry", - "signInWallets", - "safeWallets", - "kycStatus", - "availableFeatures", - "cards", - "bankingDetails", - "isSourceOfFundsAnswered", - "isPhoneValidated" - ], - "description": "User data structure", - "example": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a user's KYC status changes.", - "example": { - "eventType": "kyc.status.changed", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "kyc.status.changed", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } -} -``` - -## User Lifecycle Events - -### user.created - -Triggered when a new user account is created in the Gnosis Pay system. - -**Event Type**: `user.created` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "user.created"; - data: { - /** Unique user identifier */ - id: string; - /** User email address */ - email: string; - /** User phone number */ - phone: string; - /** User first name */ - firstName: string; - /** User last name */ - lastName: string; - /** Primary address line */ - address1: string; - /** Secondary address line */ - address2: string; - /** City */ - city: string; - /** Postal/ZIP code */ - postalCode: string; - /** State/Province */ - state: string; - /** Country */ - country: string; - /** Nationality country */ - nationalityCountry: string; - /** User's sign-in wallet addresses */ - signInWallets: { - /** Wallet address */ - address: string; - }[]; - /** User's safe wallet addresses */ - safeWallets: { - /** Safe wallet address */ - address: string; - /** Blockchain chain ID */ - chainId: string; - }[]; - /** KYC verification status */ - kycStatus: "notStarted" | "documentsRequested" | "pending" | "processing" | "approved" | "resubmissionRequested" | "rejected" | "requiresAction"; - /** Available features for the user */ - availableFeatures: { - /** Whether Monerium IBAN is available */ - moneriumIban: boolean; - }; - /** User's cards */ - cards: { - /** Card ID */ - id: string; - /** Card token */ - cardToken: string; - /** Last four digits of card */ - lastFourDigits: string; - /** Name embossed on card */ - embossedName: string; - /** Card activation timestamp */ - activatedAt: string; - /** Whether card is virtual */ - virtual: boolean; - }[]; - /** User's banking details */ - bankingDetails: { - /** Monerium IBAN */ - moneriumIban: string; - /** Monerium BIC */ - moneriumBic: string; - /** Monerium IBAN status */ - moneriumIbanStatus: string; - /** Banking address */ - address: string; - }; - /** Whether source of funds question was answered */ - isSourceOfFundsAnswered: boolean; - /** Whether phone number is validated */ - isPhoneValidated: boolean; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "user.created Webhook Event", - "version": "1.0.0", - "description": "Triggered when a new user account is created in the Gnosis Pay system." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "user.created" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique user identifier" - }, - "email": { - "type": "string", - "nullable": true, - "description": "User email address" - }, - "phone": { - "type": "string", - "nullable": true, - "description": "User phone number" - }, - "firstName": { - "type": "string", - "nullable": true, - "description": "User first name" - }, - "lastName": { - "type": "string", - "nullable": true, - "description": "User last name" - }, - "address1": { - "type": "string", - "nullable": true, - "description": "Primary address line" - }, - "address2": { - "type": "string", - "nullable": true, - "description": "Secondary address line" - }, - "city": { - "type": "string", - "nullable": true, - "description": "City" - }, - "postalCode": { - "type": "string", - "nullable": true, - "description": "Postal/ZIP code" - }, - "state": { - "type": "string", - "nullable": true, - "description": "State/Province" - }, - "country": { - "type": "string", - "nullable": true, - "description": "Country" - }, - "nationalityCountry": { - "type": "string", - "nullable": true, - "description": "Nationality country" - }, - "signInWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Wallet address" - } - }, - "required": [ - "address" - ] - }, - "description": "User's sign-in wallet addresses" - }, - "safeWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Safe wallet address" - }, - "chainId": { - "type": "string", - "description": "Blockchain chain ID" - } - }, - "required": [ - "address", - "chainId" - ] - }, - "description": "User's safe wallet addresses" - }, - "kycStatus": { - "type": "string", - "enum": [ - "notStarted", - "documentsRequested", - "pending", - "processing", - "approved", - "resubmissionRequested", - "rejected", - "requiresAction" - ], - "description": "KYC verification status", - "example": "approved" - }, - "availableFeatures": { - "type": "object", - "properties": { - "moneriumIban": { - "type": "boolean", - "description": "Whether Monerium IBAN is available" - } - }, - "required": [ - "moneriumIban" - ], - "description": "Available features for the user" - }, - "cards": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Card ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "lastFourDigits": { - "type": "string", - "description": "Last four digits of card" - }, - "embossedName": { - "type": "string", - "nullable": true, - "description": "Name embossed on card" - }, - "activatedAt": { - "type": "string", - "nullable": true, - "description": "Card activation timestamp" - }, - "virtual": { - "type": "boolean", - "description": "Whether card is virtual" - } - }, - "required": [ - "id", - "cardToken", - "lastFourDigits", - "embossedName", - "activatedAt", - "virtual" - ] - }, - "description": "User's cards" - }, - "bankingDetails": { - "type": "object", - "nullable": true, - "properties": { - "moneriumIban": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN" - }, - "moneriumBic": { - "type": "string", - "nullable": true, - "description": "Monerium BIC" - }, - "moneriumIbanStatus": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN status" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Banking address" - } - }, - "required": [ - "moneriumIban", - "moneriumBic", - "moneriumIbanStatus", - "address" - ], - "description": "User's banking details" - }, - "isSourceOfFundsAnswered": { - "type": "boolean", - "description": "Whether source of funds question was answered" - }, - "isPhoneValidated": { - "type": "boolean", - "description": "Whether phone number is validated" - } - }, - "required": [ - "id", - "email", - "phone", - "firstName", - "lastName", - "address1", - "address2", - "city", - "postalCode", - "state", - "country", - "nationalityCountry", - "signInWallets", - "safeWallets", - "kycStatus", - "availableFeatures", - "cards", - "bankingDetails", - "isSourceOfFundsAnswered", - "isPhoneValidated" - ], - "description": "User data structure", - "example": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a new user account is created in the Gnosis Pay system.", - "example": { - "eventType": "user.created", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "user.created", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } -} -``` - -### user.tos.accepted - -Triggered when a user accepts the Terms of Service. - -**Event Type**: `user.tos.accepted` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "user.tos.accepted"; - data: { - /** Unique user identifier */ - id: string; - /** User email address */ - email: string; - /** User phone number */ - phone: string; - /** User first name */ - firstName: string; - /** User last name */ - lastName: string; - /** Primary address line */ - address1: string; - /** Secondary address line */ - address2: string; - /** City */ - city: string; - /** Postal/ZIP code */ - postalCode: string; - /** State/Province */ - state: string; - /** Country */ - country: string; - /** Nationality country */ - nationalityCountry: string; - /** User's sign-in wallet addresses */ - signInWallets: { - /** Wallet address */ - address: string; - }[]; - /** User's safe wallet addresses */ - safeWallets: { - /** Safe wallet address */ - address: string; - /** Blockchain chain ID */ - chainId: string; - }[]; - /** KYC verification status */ - kycStatus: "notStarted" | "documentsRequested" | "pending" | "processing" | "approved" | "resubmissionRequested" | "rejected" | "requiresAction"; - /** Available features for the user */ - availableFeatures: { - /** Whether Monerium IBAN is available */ - moneriumIban: boolean; - }; - /** User's cards */ - cards: { - /** Card ID */ - id: string; - /** Card token */ - cardToken: string; - /** Last four digits of card */ - lastFourDigits: string; - /** Name embossed on card */ - embossedName: string; - /** Card activation timestamp */ - activatedAt: string; - /** Whether card is virtual */ - virtual: boolean; - }[]; - /** User's banking details */ - bankingDetails: { - /** Monerium IBAN */ - moneriumIban: string; - /** Monerium BIC */ - moneriumBic: string; - /** Monerium IBAN status */ - moneriumIbanStatus: string; - /** Banking address */ - address: string; - }; - /** Whether source of funds question was answered */ - isSourceOfFundsAnswered: boolean; - /** Whether phone number is validated */ - isPhoneValidated: boolean; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "user.tos.accepted Webhook Event", - "version": "1.0.0", - "description": "Triggered when a user accepts the Terms of Service." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "user.tos.accepted" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique user identifier" - }, - "email": { - "type": "string", - "nullable": true, - "description": "User email address" - }, - "phone": { - "type": "string", - "nullable": true, - "description": "User phone number" - }, - "firstName": { - "type": "string", - "nullable": true, - "description": "User first name" - }, - "lastName": { - "type": "string", - "nullable": true, - "description": "User last name" - }, - "address1": { - "type": "string", - "nullable": true, - "description": "Primary address line" - }, - "address2": { - "type": "string", - "nullable": true, - "description": "Secondary address line" - }, - "city": { - "type": "string", - "nullable": true, - "description": "City" - }, - "postalCode": { - "type": "string", - "nullable": true, - "description": "Postal/ZIP code" - }, - "state": { - "type": "string", - "nullable": true, - "description": "State/Province" - }, - "country": { - "type": "string", - "nullable": true, - "description": "Country" - }, - "nationalityCountry": { - "type": "string", - "nullable": true, - "description": "Nationality country" - }, - "signInWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Wallet address" - } - }, - "required": [ - "address" - ] - }, - "description": "User's sign-in wallet addresses" - }, - "safeWallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Safe wallet address" - }, - "chainId": { - "type": "string", - "description": "Blockchain chain ID" - } - }, - "required": [ - "address", - "chainId" - ] - }, - "description": "User's safe wallet addresses" - }, - "kycStatus": { - "type": "string", - "enum": [ - "notStarted", - "documentsRequested", - "pending", - "processing", - "approved", - "resubmissionRequested", - "rejected", - "requiresAction" - ], - "description": "KYC verification status", - "example": "approved" - }, - "availableFeatures": { - "type": "object", - "properties": { - "moneriumIban": { - "type": "boolean", - "description": "Whether Monerium IBAN is available" - } - }, - "required": [ - "moneriumIban" - ], - "description": "Available features for the user" - }, - "cards": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Card ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "lastFourDigits": { - "type": "string", - "description": "Last four digits of card" - }, - "embossedName": { - "type": "string", - "nullable": true, - "description": "Name embossed on card" - }, - "activatedAt": { - "type": "string", - "nullable": true, - "description": "Card activation timestamp" - }, - "virtual": { - "type": "boolean", - "description": "Whether card is virtual" - } - }, - "required": [ - "id", - "cardToken", - "lastFourDigits", - "embossedName", - "activatedAt", - "virtual" - ] - }, - "description": "User's cards" - }, - "bankingDetails": { - "type": "object", - "nullable": true, - "properties": { - "moneriumIban": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN" - }, - "moneriumBic": { - "type": "string", - "nullable": true, - "description": "Monerium BIC" - }, - "moneriumIbanStatus": { - "type": "string", - "nullable": true, - "description": "Monerium IBAN status" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Banking address" - } - }, - "required": [ - "moneriumIban", - "moneriumBic", - "moneriumIbanStatus", - "address" - ], - "description": "User's banking details" - }, - "isSourceOfFundsAnswered": { - "type": "boolean", - "description": "Whether source of funds question was answered" - }, - "isPhoneValidated": { - "type": "boolean", - "description": "Whether phone number is validated" - } - }, - "required": [ - "id", - "email", - "phone", - "firstName", - "lastName", - "address1", - "address2", - "city", - "postalCode", - "state", - "country", - "nationalityCountry", - "signInWallets", - "safeWallets", - "kycStatus", - "availableFeatures", - "cards", - "bankingDetails", - "isSourceOfFundsAnswered", - "isPhoneValidated" - ], - "description": "User data structure", - "example": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a user accepts the Terms of Service.", - "example": { - "eventType": "user.tos.accepted", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "user.tos.accepted", - "data": { - "id": "user_123", - "email": "user@example.com", - "phone": "+1234567890", - "firstName": "John", - "lastName": "Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "nationalityCountry": "US", - "signInWallets": [ - { - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - ], - "safeWallets": [ - { - "address": "0xabcdef1234567890abcdef1234567890abcdef12", - "chainId": "100" - } - ], - "kycStatus": "approved", - "availableFeatures": { - "moneriumIban": true - }, - "cards": [ - { - "id": "card_123", - "cardToken": "card_token_456", - "lastFourDigits": "1234", - "embossedName": "John Doe", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": false - } - ], - "bankingDetails": { - "moneriumIban": "GB82WEST12345698765432", - "moneriumBic": "WESTGB22", - "moneriumIbanStatus": "ASSIGNED", - "address": "123 Main Street, New York, NY 10001" - }, - "isSourceOfFundsAnswered": true, - "isPhoneValidated": true - } -} -``` - -## Card Events - -### card.status.changed - -Triggered when a card's status changes. - -**Event Type**: `card.status.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.status.changed"; - data: { - /** User ID */ - userId: string; - /** Card token */ - cardToken: string; - /** Previous card status */ - oldStatus: string; - /** New card status */ - newStatus: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.status.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when a card's status changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.status.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "oldStatus": { - "type": "string", - "description": "Previous card status" - }, - "newStatus": { - "type": "string", - "description": "New card status" - } - }, - "required": [ - "userId", - "cardToken", - "oldStatus", - "newStatus" - ], - "description": "Card status change data", - "example": { - "userId": "user_123", - "cardToken": "card_123", - "oldStatus": "inactive", - "newStatus": "active" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a card's status changes.", - "example": { - "eventType": "card.status.changed", - "data": { - "userId": "user_123", - "cardToken": "card_123", - "oldStatus": "inactive", - "newStatus": "active" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.status.changed", - "data": { - "userId": "user_123", - "cardToken": "card_123", - "oldStatus": "inactive", - "newStatus": "active" - } -} -``` - -### card.transaction.cleared - -Triggered when a new card transaction is cleared. - -**Event Type**: `card.transaction.cleared` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.cleared"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.cleared Webhook Event", - "version": "1.0.0", - "description": "Triggered when a new card transaction is cleared." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.cleared" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a new card transaction is cleared.", - "example": { - "eventType": "card.transaction.cleared", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.cleared", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - -### card.transaction.created - -Triggered when a new card transaction is created. - -**Event Type**: `card.transaction.created` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.created"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.created Webhook Event", - "version": "1.0.0", - "description": "Triggered when a new card transaction is created." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.created" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a new card transaction is created.", - "example": { - "eventType": "card.transaction.created", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.created", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - - -### card.transaction.confirmed - -Triggered when a blockchain transaction is confirmed. - -**Event Type**: `card.transaction.confirmed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.confirmed"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.confirmed Webhook Event", - "version": "1.0.0", - "description": "Triggered when the transaction hash becomes available after broadcast." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.confirmed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when the transaction hash becomes available after broadcast.", - "example": { - "eventType": "card.transaction.confirmed", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.confirmed", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - -### card.transaction.failed - -Triggered when a blockchain transaction fails. - -**Event Type**: `card.transaction.failed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.failed"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.failed Webhook Event", - "version": "1.0.0", - "description": "Triggered when the transaction hash becomes available after broadcast." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.failed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when the transaction hash becomes available after broadcast.", - "example": { - "eventType": "card.transaction.failed", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.failed", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - -### card.transaction.reversed - -Triggered when a reversal event is detected during clearing. - -**Event Type**: `card.transaction.reversed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.reversed"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.reversed Webhook Event", - "version": "1.0.0", - "description": "Triggered when the transaction hash becomes available after broadcast." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.reversed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when the transaction hash becomes available after broadcast.", - "example": { - "eventType": "card.transaction.reversed", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.reversed", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - -### card.transaction.refunded - -Triggered when a refund event is detected during clearing. - -**Event Type**: `card.transaction.refunded` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.refunded"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.refunded Webhook Event", - "version": "1.0.0", - "description": "Triggered when the transaction hash becomes available after broadcast." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.refunded" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when the transaction hash becomes available after broadcast.", - "example": { - "eventType": "card.transaction.refunded", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.refunded", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - -### card.transaction.declined - -Triggered when an authorization record is created with a declined response. - -**Event Type**: `card.transaction.declined` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "card.transaction.declined"; - data: { - /** User ID */ - userId: string; - /** Card transaction event */ - event: { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Payment"; - status: "Approved" | "IncorrectPin" | "InsufficientFunds" | "ExceedsApprovalAmountLimit" | "InvalidAmount" | "PinEntryTriesExceeded" | "IncorrectSecurityCode" | "Reversal" | "PartialReversal" | "Other"; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Refund"; - refundAmount: string; - refundCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - } | { - threadId: string; - createdAt: string; - clearedAt: string; - isPending: boolean; - country: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - mcc: string; - merchant: { - name: string; - city: string; - country?: { - name: string; - numeric: string; - alpha2: string; - alpha3: string; - }; - }; - billingAmount: string; - billingCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionAmount: string; - transactionCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - transactionType: string; - cardToken: string; - impactsCashback: boolean | null; - transactions: { - status: "Unsubmitted" | "Unconfirmed" | "ExecReverted" | "ExecSuccess"; - to: string; - value: string; - data: string; - hash: string | null; - }[]; - kind: "Reversal"; - reversalAmount: string; - reversalCurrency: { - symbol: string; - code: string; - decimals: number; - name: string; - }; - }; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "card.transaction.declined Webhook Event", - "version": "1.0.0", - "description": "Triggered when the transaction hash becomes available after broadcast." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "card.transaction.declined" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "event": { - "anyOf": [ - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Payment" - ] - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "IncorrectPin", - "InsufficientFunds", - "ExceedsApprovalAmountLimit", - "InvalidAmount", - "PinEntryTriesExceeded", - "IncorrectSecurityCode", - "Reversal", - "PartialReversal", - "Other" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "status" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Refund" - ] - }, - "refundAmount": { - "type": "string" - }, - "refundCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "refundAmount", - "refundCurrency" - ] - }, - { - "type": "object", - "properties": { - "threadId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "clearedAt": { - "type": "string", - "nullable": true - }, - "isPending": { - "type": "boolean" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - }, - "mcc": { - "type": "string" - }, - "merchant": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "city": { - "type": "string" - }, - "country": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "numeric": { - "type": "string" - }, - "alpha2": { - "type": "string" - }, - "alpha3": { - "type": "string" - } - }, - "required": [ - "name", - "numeric", - "alpha2", - "alpha3" - ] - } - }, - "required": [ - "name", - "city" - ] - }, - "billingAmount": { - "type": "string" - }, - "billingCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionAmount": { - "type": "string" - }, - "transactionCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - }, - "transactionType": { - "type": "string" - }, - "cardToken": { - "type": "string" - }, - "impactsCashback": { - "type": "boolean", - "nullable": true - }, - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Unsubmitted", - "Unconfirmed", - "ExecReverted", - "ExecSuccess" - ] - }, - "to": { - "type": "string" - }, - "value": { - "type": "string" - }, - "data": { - "type": "string" - }, - "hash": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "to", - "value", - "data" - ] - } - }, - "kind": { - "type": "string", - "enum": [ - "Reversal" - ] - }, - "reversalAmount": { - "type": "string" - }, - "reversalCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "code": { - "type": "string" - }, - "decimals": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "required": [ - "symbol", - "code", - "decimals", - "name" - ] - } - }, - "required": [ - "threadId", - "createdAt", - "clearedAt", - "isPending", - "country", - "mcc", - "merchant", - "billingAmount", - "billingCurrency", - "transactionAmount", - "transactionCurrency", - "transactionType", - "cardToken", - "transactions", - "kind", - "reversalAmount", - "reversalCurrency" - ] - } - ], - "example": { - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - }, - "description": "Card transaction event" - } - }, - "required": [ - "userId", - "event" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when the transaction hash becomes available after broadcast.", - "example": { - "eventType": "card.transaction.declined", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "card.transaction.declined", - "data": { - "userId": "user_123", - "event": { - "kind": "Payment", - "threadId": "thread_123456789", - "createdAt": "2024-01-15T10:30:00.000Z", - "clearedAt": "2024-01-15T10:35:00.000Z", - "isPending": false, - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - }, - "mcc": "5411", - "merchant": { - "name": "Grocery Store Inc", - "city": "New York", - "country": { - "name": "United States", - "numeric": "840", - "alpha2": "US", - "alpha3": "USA" - } - }, - "billingAmount": "5000", - "billingCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionAmount": "5000", - "transactionCurrency": { - "symbol": "$", - "code": "840", - "decimals": 2, - "name": "US Dollar" - }, - "transactionType": "purchase", - "cardToken": "card_token_abc123", - "impactsCashback": true, - "status": "Approved", - "transactions": [ - { - "status": "ExecSuccess", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", - "value": "5000000000000000000", - "data": "0x", - "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - } - ] - } - } -} -``` - - -### physical.card.activated - -Triggered when a physical card is activated. - -**Event Type**: `physical.card.activated` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "physical.card.activated"; - data: { - /** User ID */ - userId: string; - /** Unique card token identifier */ - cardToken: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "physical.card.activated Webhook Event", - "version": "1.0.0", - "description": "Triggered when a physical card is activated." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "physical.card.activated" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "cardToken": { - "type": "string", - "description": "Unique card token identifier" - } - }, - "required": [ - "userId", - "cardToken" - ], - "description": "Card token data", - "example": { - "userId": "user_123", - "cardToken": "card_123456789" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a physical card is activated.", - "example": { - "eventType": "physical.card.activated", - "data": { - "userId": "user_123", - "cardToken": "card_123456789" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "physical.card.activated", - "data": { - "userId": "user_123", - "cardToken": "card_123456789" - } -} -``` - -### physical.card.order.status.changed - -Triggered when the status of a physical card order changes. - -**Event Type**: `physical.card.order.status.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "physical.card.order.status.changed"; - data: { - /** User ID */ - userId: string; - /** Card token */ - cardToken: string; - /** Card order ID */ - cardOrderId: string; - /** Previous order status */ - oldStatus: string; - /** New order status */ - newStatus: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "physical.card.order.status.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when the status of a physical card order changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "physical.card.order.status.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "cardOrderId": { - "type": "string", - "description": "Card order ID" - }, - "oldStatus": { - "type": "string", - "description": "Previous order status" - }, - "newStatus": { - "type": "string", - "description": "New order status" - } - }, - "required": [ - "userId", - "cardToken", - "cardOrderId", - "oldStatus", - "newStatus" - ], - "description": "Physical card order status change data", - "example": { - "userId": "user_123", - "cardToken": "card_123", - "cardOrderId": "order_456", - "oldStatus": "ordered", - "newStatus": "shipped" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when the status of a physical card order changes.", - "example": { - "eventType": "physical.card.order.status.changed", - "data": { - "userId": "user_123", - "cardToken": "card_123", - "cardOrderId": "order_456", - "oldStatus": "ordered", - "newStatus": "shipped" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "physical.card.order.status.changed", - "data": { - "userId": "user_123", - "cardToken": "card_123", - "cardOrderId": "order_456", - "oldStatus": "ordered", - "newStatus": "shipped" - } -} -``` - -### physical.card.ordered - -Triggered when a physical card is ordered. - -**Event Type**: `physical.card.ordered` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "physical.card.ordered"; - data: { - /** User ID */ - userId: string; - /** Card token */ - cardToken: string; - /** Card order ID */ - id: string; - /** Order status */ - status: string; - /** Name to emboss on card */ - embossedName: string; - /** Primary address */ - address1: string; - /** Secondary address */ - address2: string; - /** City */ - city: string; - /** Postal code */ - postalCode: string; - /** State */ - state: string; - /** Country */ - country: string; - /** Phone number */ - phone: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "physical.card.ordered Webhook Event", - "version": "1.0.0", - "description": "Triggered when a physical card is ordered." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "physical.card.ordered" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "cardToken": { - "type": "string", - "description": "Card token" - }, - "id": { - "type": "string", - "description": "Card order ID" - }, - "status": { - "type": "string", - "description": "Order status" - }, - "embossedName": { - "type": "string", - "description": "Name to emboss on card" - }, - "address1": { - "type": "string", - "nullable": true, - "description": "Primary address" - }, - "address2": { - "type": "string", - "nullable": true, - "description": "Secondary address" - }, - "city": { - "type": "string", - "nullable": true, - "description": "City" - }, - "postalCode": { - "type": "string", - "nullable": true, - "description": "Postal code" - }, - "state": { - "type": "string", - "nullable": true, - "description": "State" - }, - "country": { - "type": "string", - "nullable": true, - "description": "Country" - }, - "phone": { - "type": "string", - "nullable": true, - "description": "Phone number" - } - }, - "required": [ - "userId", - "cardToken", - "id", - "status", - "embossedName", - "address1", - "address2", - "city", - "postalCode", - "state", - "country", - "phone" - ], - "description": "Physical card order data", - "example": { - "userId": "user_123", - "cardToken": "card_123", - "id": "order_456", - "status": "ordered", - "embossedName": "John Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "phone": "+1234567890" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a physical card is ordered.", - "example": { - "eventType": "physical.card.ordered", - "data": { - "userId": "user_123", - "cardToken": "card_123", - "id": "order_456", - "status": "ordered", - "embossedName": "John Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "phone": "+1234567890" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "physical.card.ordered", - "data": { - "userId": "user_123", - "cardToken": "card_123", - "id": "order_456", - "status": "ordered", - "embossedName": "John Doe", - "address1": "123 Main Street", - "address2": "Apt 4B", - "city": "New York", - "postalCode": "10001", - "state": "NY", - "country": "US", - "phone": "+1234567890" - } -} -``` - -### physical.card.pin.changed - -Triggered when a physical card's PIN is changed. - -**Event Type**: `physical.card.pin.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "physical.card.pin.changed"; - data: { - /** User ID */ - userId: string; - /** Unique card token identifier */ - cardToken: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "physical.card.pin.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when a physical card's PIN is changed." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "physical.card.pin.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "cardToken": { - "type": "string", - "description": "Unique card token identifier" - } - }, - "required": [ - "userId", - "cardToken" - ], - "description": "Card token data", - "example": { - "userId": "user_123", - "cardToken": "card_123456789" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a physical card's PIN is changed.", - "example": { - "eventType": "physical.card.pin.changed", - "data": { - "userId": "user_123", - "cardToken": "card_123456789" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "physical.card.pin.changed", - "data": { - "userId": "user_123", - "cardToken": "card_123456789" - } -} -``` - -### virtual.card.issued - -Triggered when a virtual card is issued. - -**Event Type**: `virtual.card.issued` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "virtual.card.issued"; - data: { - /** Card ID */ - id: string; - /** Unique card token identifier */ - cardToken: string; - /** User ID */ - userId: string; - /** Last four digits of the card */ - lastFourDigits: string; - /** When the card was activated (ISO 8601) */ - activatedAt: string | null; - /** True for virtual cards, false for physical */ - virtual: boolean; - /** Card status code from payment processor (e.g. 1000 = Active) */ - statusCode: number; - /** Human-readable card status (e.g. Active, Frozen, Void) */ - statusName: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "virtual.card.issued Webhook Event", - "version": "1.0.0", - "description": "Triggered when a virtual card is issued." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "virtual.card.issued" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Card ID" - }, - "cardToken": { - "type": "string", - "description": "Unique card token identifier" - }, - "userId": { - "type": "string", - "description": "User ID" - }, - "lastFourDigits": { - "type": "string", - "description": "Last four digits of the card" - }, - "activatedAt": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the card was activated (ISO 8601)" - }, - "virtual": { - "type": "boolean", - "description": "True for virtual cards, false for physical" - }, - "statusCode": { - "type": "number", - "description": "Card status code from payment processor (e.g. 1000 = Active)" - }, - "statusName": { - "type": "string", - "description": "Human-readable card status (e.g. Active, Frozen, Void)" - } - }, - "required": [ - "id", - "cardToken", - "userId", - "lastFourDigits", - "activatedAt", - "virtual", - "statusCode", - "statusName" - ], - "description": "Card data payload for card-related webhook events.", - "example": { - "id": "card_abc123", - "userId": "user_123", - "cardToken": "card_123456789", - "lastFourDigits": "4242", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": true, - "statusCode": 1000, - "statusName": "Active" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a virtual card is issued.", - "example": { - "eventType": "virtual.card.issued", - "data": { - "id": "card_abc123", - "userId": "user_123", - "cardToken": "card_123456789", - "lastFourDigits": "4242", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": true, - "statusCode": 1000, - "statusName": "Active" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "virtual.card.issued", - "data": { - "id": "card_abc123", - "userId": "user_123", - "cardToken": "card_123456789", - "lastFourDigits": "4242", - "activatedAt": "2024-01-15T10:30:00Z", - "virtual": true, - "statusCode": 1000, - "statusName": "Active" - } -} -``` - -## Account Events - -### account.balance.changed - -Triggered when an account's balance changes. - -**Event Type**: `account.balance.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "account.balance.changed"; - data: { - /** User ID */ - userId: string; - /** Total balance in wei */ - total: number; - /** Spendable balance in wei */ - spendable: number; - /** Pending balance in wei */ - pending: number; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "account.balance.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when an account's balance changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "account.balance.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "total": { - "type": "number", - "description": "Total balance in wei" - }, - "spendable": { - "type": "number", - "description": "Spendable balance in wei" - }, - "pending": { - "type": "number", - "description": "Pending balance in wei" - } - }, - "required": [ - "userId", - "total", - "spendable", - "pending" - ], - "description": "Account balance change data", - "example": { - "userId": "user_123", - "total": 1000000000000000000, - "spendable": 800000000000000000, - "pending": 200000000000000000 - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an account's balance changes.", - "example": { - "eventType": "account.balance.changed", - "data": { - "userId": "user_123", - "total": 1000000000000000000, - "spendable": 800000000000000000, - "pending": 200000000000000000 - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "account.balance.changed", - "data": { - "userId": "user_123", - "total": 1000000000000000000, - "spendable": 800000000000000000, - "pending": 200000000000000000 - } -} -``` - -### account.limit.changed - -Triggered when an account's limit changes. - -**Event Type**: `account.limit.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "account.limit.changed"; - data: { - /** User ID */ - userId: string; - /** Previous account limit */ - oldLimit: number; - /** New account limit */ - newLimit: number; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "account.limit.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when an account's limit changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "account.limit.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "oldLimit": { - "type": "number", - "description": "Previous account limit" - }, - "newLimit": { - "type": "number", - "description": "New account limit" - } - }, - "required": [ - "userId", - "oldLimit", - "newLimit" - ], - "description": "Account limit change data", - "example": { - "userId": "user_123", - "oldLimit": 1000, - "newLimit": 2000 - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an account's limit changes.", - "example": { - "eventType": "account.limit.changed", - "data": { - "userId": "user_123", - "oldLimit": 1000, - "newLimit": 2000 - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "account.limit.changed", - "data": { - "userId": "user_123", - "oldLimit": 1000, - "newLimit": 2000 - } -} -``` - -### account.withdrawal.completed - -Triggered when an account withdrawal is completed. - -**Event Type**: `account.withdrawal.completed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "account.withdrawal.completed"; - data: { - /** Transaction ID */ - id: string; - /** Safe address */ - safeAddress: string; - /** Transaction data */ - transactionData: string; - /** Enqueue task ID */ - enqueueTaskId: string; - /** Dispatch task ID */ - dispatchTaskId: string; - /** Ready timestamp */ - readyAt: string; - /** Operation type for delay transaction */ - operationType: "CALL" | "DELEGATECALL"; - /** User ID */ - userId: string; - /** Delay transaction status */ - status: "QUEUING" | "WAITING" | "EXECUTING" | "EXECUTED" | "FAILED"; - /** Creation timestamp */ - createdAt: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "account.withdrawal.completed Webhook Event", - "version": "1.0.0", - "description": "Triggered when an account withdrawal is completed." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "account.withdrawal.completed" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Transaction ID" - }, - "safeAddress": { - "type": "string", - "description": "Safe address" - }, - "transactionData": { - "type": "string", - "description": "Transaction data" - }, - "enqueueTaskId": { - "type": "string", - "description": "Enqueue task ID" - }, - "dispatchTaskId": { - "type": "string", - "nullable": true, - "description": "Dispatch task ID" - }, - "readyAt": { - "type": "string", - "nullable": true, - "format": "date", - "description": "Ready timestamp" - }, - "operationType": { - "type": "string", - "nullable": true, - "enum": [ - "CALL", - "DELEGATECALL" - ], - "description": "Operation type for delay transaction", - "example": "CALL" - }, - "userId": { - "type": "string", - "description": "User ID" - }, - "status": { - "type": "string", - "enum": [ - "QUEUING", - "WAITING", - "EXECUTING", - "EXECUTED", - "FAILED" - ], - "description": "Delay transaction status", - "example": "EXECUTED" - }, - "createdAt": { - "type": "string", - "format": "date", - "description": "Creation timestamp" - } - }, - "required": [ - "id", - "safeAddress", - "transactionData", - "enqueueTaskId", - "dispatchTaskId", - "readyAt", - "operationType", - "userId", - "status", - "createdAt" - ], - "description": "Delay transaction data", - "example": { - "id": "tx_123", - "safeAddress": "0x123...", - "transactionData": "0x456...", - "enqueueTaskId": "task_789", - "dispatchTaskId": "task_101", - "readyAt": "2025-10-15T08:00:24.194Z", - "operationType": "CALL", - "userId": "user_123", - "status": "EXECUTED", - "createdAt": "2025-10-15T08:00:24.194Z" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an account withdrawal is completed.", - "example": { - "eventType": "account.withdrawal.completed", - "data": { - "id": "tx_123", - "safeAddress": "0x123...", - "transactionData": "0x456...", - "enqueueTaskId": "task_789", - "dispatchTaskId": "task_101", - "readyAt": "2025-10-15T08:00:24.194Z", - "operationType": "CALL", - "userId": "user_123", - "status": "EXECUTED", - "createdAt": "2025-10-15T08:00:24.194Z" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "account.withdrawal.completed", - "data": { - "id": "tx_123", - "safeAddress": "0x123...", - "transactionData": "0x456...", - "enqueueTaskId": "task_789", - "dispatchTaskId": "task_101", - "readyAt": "2025-10-15T08:00:24.194Z", - "operationType": "CALL", - "userId": "user_123", - "status": "EXECUTED", - "createdAt": "2025-10-15T08:00:24.194Z" - } -} -``` - -### account.withdrawal.failed - -Triggered when an account withdrawal fails. - -**Event Type**: `account.withdrawal.failed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "account.withdrawal.failed"; - data: { - /** User ID */ - userId: string; - /** Error message */ - error: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "account.withdrawal.failed Webhook Event", - "version": "1.0.0", - "description": "Triggered when an account withdrawal fails." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "account.withdrawal.failed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "error": { - "type": "string", - "description": "Error message" - } - }, - "required": [ - "userId", - "error" - ], - "description": "Error data", - "example": { - "userId": "user_123", - "error": "Transaction failed" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an account withdrawal fails.", - "example": { - "eventType": "account.withdrawal.failed", - "data": { - "userId": "user_123", - "error": "Transaction failed" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "account.withdrawal.failed", - "data": { - "userId": "user_123", - "error": "Transaction failed" - } -} -``` - -## Banking Events - -### iban.created - -Triggered when an IBAN is created for a user. - -**Event Type**: `iban.created` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "iban.created"; - data: { - /** User ID */ - userId: string; - /** International Bank Account Number */ - iban: string; - /** Bank Identifier Code */ - bic: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "iban.created Webhook Event", - "version": "1.0.0", - "description": "Triggered when an IBAN is created for a user." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "iban.created" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "iban": { - "type": "string", - "description": "International Bank Account Number" - }, - "bic": { - "type": "string", - "description": "Bank Identifier Code" - } - }, - "required": [ - "userId", - "iban", - "bic" - ], - "description": "IBAN data", - "example": { - "userId": "user_123", - "iban": "GB82WEST12345698765432", - "bic": "WESTGB22" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an IBAN is created for a user.", - "example": { - "eventType": "iban.created", - "data": { - "userId": "user_123", - "iban": "GB82WEST12345698765432", - "bic": "WESTGB22" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "iban.created", - "data": { - "userId": "user_123", - "iban": "GB82WEST12345698765432", - "bic": "WESTGB22" - } -} -``` - -### iban.status.changed - -Triggered when an IBAN's status changes. - -**Event Type**: `iban.status.changed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "iban.status.changed"; - data: { - /** User ID */ - userId: string; - /** IBAN status */ - previousStatus: "NOTSTARTED" | "PENDING" | "PENDING_OAUTH" | "ASSIGNED"; - /** IBAN status */ - currentStatus: "NOTSTARTED" | "PENDING" | "PENDING_OAUTH" | "ASSIGNED"; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "iban.status.changed Webhook Event", - "version": "1.0.0", - "description": "Triggered when an IBAN's status changes." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "iban.status.changed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "previousStatus": { - "type": "string", - "enum": [ - "NOTSTARTED", - "PENDING", - "PENDING_OAUTH", - "ASSIGNED" - ], - "description": "IBAN status", - "example": "ASSIGNED" - }, - "currentStatus": { - "type": "string", - "enum": [ - "NOTSTARTED", - "PENDING", - "PENDING_OAUTH", - "ASSIGNED" - ], - "description": "IBAN status", - "example": "ASSIGNED" - } - }, - "required": [ - "userId", - "previousStatus", - "currentStatus" - ], - "description": "IBAN status change data", - "example": { - "userId": "user_123", - "previousStatus": "PENDING", - "currentStatus": "ASSIGNED" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an IBAN's status changes.", - "example": { - "eventType": "iban.status.changed", - "data": { - "userId": "user_123", - "previousStatus": "PENDING", - "currentStatus": "ASSIGNED" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "iban.status.changed", - "data": { - "userId": "user_123", - "previousStatus": "PENDING", - "currentStatus": "ASSIGNED" - } -} -``` - -## Safe Wallet Events - -### safe.created - -Triggered when a Safe wallet is created. - -**Event Type**: `safe.created` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "safe.created"; - data: { - /** Safe account ID */ - id: string; - /** Safe account address */ - address: string; - /** Account salt */ - salt: string; - /** Blockchain chain ID */ - chainId: string; - /** User ID */ - userId: string; - /** Token symbol */ - tokenSymbol: "GBPe" | "EURe" | "USDCe"; - /** Creation timestamp */ - createdAt: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "safe.created Webhook Event", - "version": "1.0.0", - "description": "Triggered when a Safe wallet is created." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "safe.created" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Safe account ID" - }, - "address": { - "type": "string", - "description": "Safe account address" - }, - "salt": { - "type": "string", - "nullable": true, - "description": "Account salt" - }, - "chainId": { - "type": "string", - "nullable": true, - "description": "Blockchain chain ID" - }, - "userId": { - "type": "string", - "description": "User ID" - }, - "tokenSymbol": { - "type": "string", - "nullable": true, - "enum": [ - "GBPe", - "EURe", - "USDCe" - ], - "description": "Token symbol", - "example": "EURe" - }, - "createdAt": { - "type": "string", - "description": "Creation timestamp" - } - }, - "required": [ - "id", - "address", - "salt", - "chainId", - "userId", - "tokenSymbol", - "createdAt" - ], - "description": "Safe account data", - "example": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a Safe wallet is created.", - "example": { - "eventType": "safe.created", - "data": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "safe.created", - "data": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } -} -``` - -### safe.currency.updated - -Triggered when a Safe's currency is updated. - -**Event Type**: `safe.currency.updated` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "safe.currency.updated"; - data: { - /** Safe account ID */ - id: string; - /** Safe account address */ - address: string; - /** Account salt */ - salt: string; - /** Blockchain chain ID */ - chainId: string; - /** User ID */ - userId: string; - /** Token symbol */ - tokenSymbol: "GBPe" | "EURe" | "USDCe"; - /** Creation timestamp */ - createdAt: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "safe.currency.updated Webhook Event", - "version": "1.0.0", - "description": "Triggered when a Safe's currency is updated." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "safe.currency.updated" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Safe account ID" - }, - "address": { - "type": "string", - "description": "Safe account address" - }, - "salt": { - "type": "string", - "nullable": true, - "description": "Account salt" - }, - "chainId": { - "type": "string", - "nullable": true, - "description": "Blockchain chain ID" - }, - "userId": { - "type": "string", - "description": "User ID" - }, - "tokenSymbol": { - "type": "string", - "nullable": true, - "enum": [ - "GBPe", - "EURe", - "USDCe" - ], - "description": "Token symbol", - "example": "EURe" - }, - "createdAt": { - "type": "string", - "description": "Creation timestamp" - } - }, - "required": [ - "id", - "address", - "salt", - "chainId", - "userId", - "tokenSymbol", - "createdAt" - ], - "description": "Safe account data", - "example": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when a Safe's currency is updated.", - "example": { - "eventType": "safe.currency.updated", - "data": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "safe.currency.updated", - "data": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } -} -``` - -### safe.modules.deployed - -Triggered when Safe modules are deployed. - -**Event Type**: `safe.modules.deployed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "safe.modules.deployed"; - data: { - /** Safe account ID */ - id: string; - /** Safe account address */ - address: string; - /** Account salt */ - salt: string; - /** Blockchain chain ID */ - chainId: string; - /** User ID */ - userId: string; - /** Token symbol */ - tokenSymbol: "GBPe" | "EURe" | "USDCe"; - /** Creation timestamp */ - createdAt: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "safe.modules.deployed Webhook Event", - "version": "1.0.0", - "description": "Triggered when Safe modules are deployed." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "safe.modules.deployed" - ] - }, - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Safe account ID" - }, - "address": { - "type": "string", - "description": "Safe account address" - }, - "salt": { - "type": "string", - "nullable": true, - "description": "Account salt" - }, - "chainId": { - "type": "string", - "nullable": true, - "description": "Blockchain chain ID" - }, - "userId": { - "type": "string", - "description": "User ID" - }, - "tokenSymbol": { - "type": "string", - "nullable": true, - "enum": [ - "GBPe", - "EURe", - "USDCe" - ], - "description": "Token symbol", - "example": "EURe" - }, - "createdAt": { - "type": "string", - "description": "Creation timestamp" - } - }, - "required": [ - "id", - "address", - "salt", - "chainId", - "userId", - "tokenSymbol", - "createdAt" - ], - "description": "Safe account data", - "example": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when Safe modules are deployed.", - "example": { - "eventType": "safe.modules.deployed", - "data": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "safe.modules.deployed", - "data": { - "id": "safe_123", - "address": "0x456...", - "salt": "salt_123", - "chainId": "100", - "userId": "user_123", - "tokenSymbol": "EURe", - "createdAt": "2025-10-15T08:00:24.193Z" - } -} -``` - -### safe.owner.added - -Triggered when an owner is added to a Safe. - -**Event Type**: `safe.owner.added` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "safe.owner.added"; - data: { - /** User ID */ - userId: string; - /** Current owner address */ - owner: string; - /** Safe address */ - safeAddress: string; - /** Newly added owner address */ - addedOwner: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "safe.owner.added Webhook Event", - "version": "1.0.0", - "description": "Triggered when an owner is added to a Safe." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "safe.owner.added" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "owner": { - "type": "string", - "description": "Current owner address" - }, - "safeAddress": { - "type": "string", - "description": "Safe address" - }, - "addedOwner": { - "type": "string", - "description": "Newly added owner address" - } - }, - "required": [ - "userId", - "owner", - "safeAddress", - "addedOwner" - ], - "description": "Safe owner added data", - "example": { - "userId": "user_123", - "owner": "0x123...", - "safeAddress": "0x456...", - "addedOwner": "0x789..." - } - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an owner is added to a Safe.", - "example": { - "eventType": "safe.owner.added", - "data": { - "userId": "user_123", - "owner": "0x123...", - "safeAddress": "0x456...", - "addedOwner": "0x789..." - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "safe.owner.added", - "data": { - "userId": "user_123", - "owner": "0x123...", - "safeAddress": "0x456...", - "addedOwner": "0x789..." - } -} -``` - -### safe.owner.removed - -Triggered when an owner is removed from a Safe. - -**Event Type**: `safe.owner.removed` - -**Schema Definition**: - - -```typescript TypeScript -{ - eventType: "safe.owner.removed"; - data: { - /** User ID */ - userId: string; - safeAddress: string; - removedOwner: string; - }; -} -``` -```json OpenAPI Schema -{ - "openapi": "3.0.0", - "info": { - "title": "safe.owner.removed Webhook Event", - "version": "1.0.0", - "description": "Triggered when an owner is removed from a Safe." - }, - "components": { - "schemas": { - "WebhookEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string", - "enum": [ - "safe.owner.removed" - ] - }, - "data": { - "type": "object", - "properties": { - "userId": { - "type": "string", - "description": "User ID" - }, - "safeAddress": { - "type": "string" - }, - "removedOwner": { - "type": "string" - } - }, - "required": [ - "userId", - "safeAddress", - "removedOwner" - ] - } - }, - "required": [ - "eventType", - "data" - ], - "description": "Triggered when an owner is removed from a Safe.", - "example": { - "eventType": "safe.owner.removed", - "data": { - "userId": "user_123", - "safeAddress": "0x456...", - "removedOwner": "0x789..." - } - } - } - } - } -} -``` - - -**Example**: - -```json -{ - "eventType": "safe.owner.removed", - "data": { - "userId": "user_123", - "safeAddress": "0x456...", - "removedOwner": "0x789..." - } -} -``` diff --git a/webhooks/getting-started.mdx b/webhooks/getting-started.mdx deleted file mode 100644 index d22e22a..0000000 --- a/webhooks/getting-started.mdx +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: "Working with Webhooks" -description: "Step-by-step guide to set up and implement webhooks in your application." ---- - -## Guidelines - -To implement webhooks in your application, follow these essential steps: - - - -Create a publicly accessible HTTP endpoint in your application that can receive POST requests. This endpoint must be available over HTTPS and return a 2xx status code to acknowledge receipt of webhook events. - - -Your webhook endpoint must be publicly accessible. For local development, use tools like [ngrok](https://ngrok.com/) to expose your local server. - - - - -Configure your webhook endpoint URL directly through the [Partners Dashboard](https://partners.gnosispay.com/) or contact your Gnosis Pay partner manager for assistance. Provide the complete HTTPS URL where you want to receive webhook notifications. - - -**Partners Dashboard**: For partners registered through the dashboard, webhook configuration is available instantly in your dashboard settings. -**Manual Setup**: If you need technical support assistance, webhook configuration typically takes 1-2 business days. - - - - -When events happen in the Gnosis Pay system, we'll send HTTP POST requests to your webhook endpoint with event data and cryptographic signatures. - -All webhooks include cryptographic signatures using **Ed25519 asymmetric cryptography**: - -- **`X-Webhook-Timestamp`**: Unix timestamp when the webhook was sent -- **`X-Webhook-Signature`**: Base64-encoded Ed25519 signature - - - **Always verify webhook signatures** before processing events. This ensures - the webhook originated from Gnosis Pay and hasn't been tampered with. - - -Retrieve the public key for signature verification from our API: - -```bash cURL -curl -X GET https://webhooks.gnosispay.com/api/v1/public-key -``` - - - - -Extract the `eventType` and `data` fields from the webhook payload. The `eventType` identifies what happened (e.g., `user.created`, `kyc.status.changed`), while `data` contains the complete entity information. - -```json -{ - "eventType": "user.created", - "data": { - "id": "user_123", - "email": "user@example.com", - // ... complete user entity - } -} -``` - -Handle each event type appropriately in your application. Since we send complete entity data, you typically won't need additional API calls to get the full context. - - -Process events idempotently to handle potential duplicates, and implement proper error handling and logging for monitoring. - - - - **Retry Policy**: If your webhook endpoint returns a non-2xx status code, we'll retry delivery up to 3 times with exponential backoff (1 minute, 5 minutes, 15 minutes). - - - - **Timeout**: Your webhook endpoint must respond within 30 seconds. Requests that exceed this timeout are considered failed and will trigger our retry mechanism. - - - - - -## Complete Example - - - -```typescript Node.js -import express from "express"; -import crypto from "crypto"; - -const app = express(); - -// Use raw body parsing for webhook signature verification -app.use("/webhook", express.raw({ type: "application/json" })); -app.use(express.json()); - -app.post("/webhook", async (req: express.Request, res: express.Response) => { - try { - // Verify the webhook signature - const isValid = await verifyWebhookSignature(req); - if (!isValid) { - return res.status(401).send("Invalid signature"); - } - - // Parse the body after verification - const payload = JSON.parse(req.body.toString()); - const { eventType, data } = payload; - await processWebhookEvent(eventType, data); - - // Acknowledge receipt - res.status(200).send("OK"); - } catch (error) { - console.error("Webhook processing error:", error); - res.status(500).send("Internal server error"); - } -}); - -async function verifyWebhookSignature(req: express.Request): Promise { - try { - const signature = req.headers["x-webhook-signature"] as string; - const timestamp = req.headers["x-webhook-timestamp"] as string; - - // Check if required headers are present - if (!timestamp || !signature) { - console.error( - "Missing required headers: x-webhook-timestamp or x-webhook-signature" - ); - return false; - } - - // Use the raw body for signature verification - const body = req.body.toString(); - - // Retrieve the public key from Gnosis Pay - const publicKeyResponse = await fetch( - "https://webhooks.gnosispay.com/api/v1/public-key" - ); - const keyData = await publicKeyResponse.json(); - - if (!keyData.success || !keyData.publicKey) { - throw new Error("Failed to fetch public key"); - } - - const { publicKey } = keyData; - - // Create the payload for verification (same as Bun implementation) - const signingPayload = `${timestamp}.${body}`; - - // Verify the signature using the same method as Bun - return crypto.verify( - null, - Buffer.from(signingPayload, "utf8"), - publicKey, - Buffer.from(signature, "base64") - ); - } catch (error) { - console.error("Signature verification failed:", error); - return false; - } -} - -async function processWebhookEvent(eventType: string, data: any) { - switch (eventType) { - case "user.created": - console.log("user.created", data); - break; - case "kyc.status.changed": - console.log("kyc.status.changed", data); - break; - case "card.transaction.created": - console.log("card.transaction.created", data); - break; - // Handle other event types - default: - console.log(`Unhandled event type: ${eventType}`); - } -} -``` - - diff --git a/webhooks/introduction.mdx b/webhooks/introduction.mdx deleted file mode 100644 index 81a7007..0000000 --- a/webhooks/introduction.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "Introduction" -description: "Learn how to integrate with Gnosis Pay webhooks to receive real-time event notifications." ---- - -Webhooks provide real-time notifications when events happen in the Gnosis Pay system. Rather than constantly polling our APIs for updates, you can configure webhook endpoints to receive instant notifications about user activities, KYC status changes, card transactions, and more. - -Our [Sign-In with Ethereum (SIWE) authentication system](/auth) requires customers to generate short-lived tokens (JWTs) that expire after 1 hour. This means your application may not always have a valid token to fetch updated data from our APIs. -To avoid problems, we send complete entity data whenever possible in our webhook payloads. - -## Enable Webhooks - -To enable webhooks for your account: - -1. **Contact your Gnosis Pay partner manager** or reach out to our technical support team -2. **Provide your webhook endpoint URL** (must be publicly accessible and use HTTPS) -3. **Wait for confirmation** that your webhook has been configured - - - **Setup Time**: Webhook configuration typically takes 1-2 business days after - you provide the required information. - - diff --git a/whybuild.mdx b/whybuild.mdx new file mode 100644 index 0000000..91e556d --- /dev/null +++ b/whybuild.mdx @@ -0,0 +1,78 @@ +--- +title: "Why Build with Gnosis Pay" +description: "What you get as a partner from user onboading to launching a full fledged card program" +--- +Gnosis Pay gives you the infrastructure to launch a card program and the flexibility to run it your way. Here's what that means in practice, for your business and for your engineering team. + +## For your business + +### Onboarding + + + Already onboard your users with Sumsub? Integrate that flow directly and share KYC into Gnosis Pay. Your users complete verification once, no duplicate KYC to get a card. + + +### Multi-chain and multi-currency + + + + Launch your card program on the chains you choose. We support the underlying infrastructure across multiple chains. Gnosis Pay infra is live on Celo and Gnosis Chain. + + + Enable your ecosystem's stablecoin as a spending currency, fully on-chain with native support. A single card can hold and spend multiple tokens. + + + EUR, USD, and GBP accounts available by default, with multi-currency support built in. + + + +### Money movement + + + + Fully self-custodial. Withdraw your balance instantly — no lock-up periods, no settlement delays. + + + Reversals are processed the moment we receive the message from the network. Funds return to the user's balance immediately, with no manual step in between. + + + +### Fees Customization + + + + Design your own cards to match your brand (subject to Visa's approval timeline). + + + Add your own markup on top of transactions. + + + Set account- and card-level spending limits per user. + + + Shape the program to your product from onboarding to card controls. + + + +### Payments experience + + + Provision cards to Apple Pay and Google Pay so users can tap to pay from day one. + + +## For developers and product teams + + + + Configure webhooks and PSE setup, and access analytics and user data so your team can own communications and user relationships directly. + + + A full sandbox to build and test against before going to production, including transaction simulations. + + + Subscribe to real-time events to power user notifications and keep your systems in sync. + + + Technical integration support available throughout your build. + +