# Welcome to OffBlocks

## Overview

OffBlocks is a hybrid platform for digital assets spending. We help wallet and DeFi companies offer trustless crypto-to-fiat experiences as well as payments and card services to their clients.

With OffBlocks you can help your users spend their crypto seamlessly by offering accounts and card payments backed by self-custody wallets with a few lines of code.

## Quick links

{% content-ref url="/pages/tJeY1pfgOm8dQHr37CM7" %}
[What we do](/overview/what-we-do)
{% endcontent-ref %}

{% content-ref url="/pages/jd9xX5nzyxJSlceeiiRC" %}
[Core Concepts](/overview/core-concepts)
{% endcontent-ref %}

{% content-ref url="/pages/8RDH7jTZMwVtZfsr0TDY" %}
[Whitepaper](/overview/whitepaper)
{% endcontent-ref %}


# What we do

We offer a hybrid platform that includes both on- and off-chain APIs. The platform allows web3 app developers to securely set up their users with banking products like debit cards and enable payments using their crypto-assets stored in multi-chain wallets.

Our solution prioritises blockchain network security while ensuring transparency and compliance with financial regulations.

{% hint style="info" %}
To learn more, check out our [Whitepaper](/overview/whitepaper).
{% endhint %}


# Core Concepts

## Account Abstraction

Account abstraction, most notably discussed in [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) by Vitalik Buterin himself, offers several benefits that enhance the Ethereum ecosystem. It allows for greater flexibility by separating the signature and validation process from the account, enabling smart contracts to handle transaction execution in a more tailored manner. This separation can simplify user experiences by making transactions more predictable and uniform across various wallets and services. Account abstraction can also lead to improved privacy and security by enabling more complex and customisable authentication methods. Furthermore, it paves the way for innovation by allowing developers to create new transaction types and logic without being constrained by existing account structures.

We leverage the fundamentals of account abstraction to setup fully managed digital accounts for users. While preserving complete control over their assets and spending, users are able to enjoy a neobank-grade UX within the wallet of their chosen DeFi app. Furthermore, with the introduction of transaction bundling and designated paymasters, we are able to optimise transaction costs, making the entire process more efficient and economical.

{% hint style="info" %}
Learn more about Account Abstraction on [ethereum.org](https://ethereum.org/en/roadmap/account-abstraction/) and [metamask.io](https://metamask.io/news/latest/account-abstraction-past-present-future/)
{% endhint %}

## Escrow Smart Contracts

Blockchain transactions and traditional payment network transactions differ in their underlying mechanisms and attributes. Blockchain transactions are final and atomic, meaning that once they are confirmed, they cannot be altered or reversed, and all parts of the transaction are completed simultaneously. This provides a high level of security and integrity. On the other hand, traditional payment network transactions, like those in systems like Visa or MasterCard, often involve complex state machines. These transactions can pass through various stages such as authorization, clearing, and settlement, making the process more intricate and time-consuming. The presence of intermediaries, multiple checks, and potential reversals add layers of complexity to traditional transactions.

Escrow smart contracts help make it easier for blockchain networks to work with traditional payment networks. They hold money safely until everyone agrees that the conditions of a transaction have been met. This helps build trust between the parties involved. These smart contracts make it possible for blockchain systems to work with regular financial systems without needing a central authority. This can mean lower fees and faster payments. By using escrow smart contracts we build a trustless bridge offering convenience of traditional payments with enhanced security provided by blockchain.

## Continuous Compliance Monitoring

As the digital asset landscape evolves, so do the rules and regulations governing its use. Implementing a system for continuous compliance monitoring helps in tracking and detecting any non-compliant activities in real time. This includes monitoring for fraudulent activity, adherence to Anti-Money Laundering (AML) laws, Know Your Customer (KYC) and Know Your Transaction (KYT) requirements. The process helps in minimising risks and ensures that both consumers and service providers are protected.

We provide continuous compliance monitoring for all transactions performed by your users, whether those transactions are on- or off-chain. This vigilant oversight ensures that all activities adhere to the relevant legal regulations and standards, creating a secure environment for both consumers and businesses. By offering this comprehensive service, we assist our partners in maintaining transparency, minimising risk and fostering trust within the complex landscape of both traditional and digital asset transactions.


# Whitepaper

## A Trustless Card Payments Protocol

### Abstract

*An ever growing variety of solutions and services based on blockchain and cryptocurrencies is available for crypto-natives, however the process of exchanging digital assets for fiat currencies, also known as off-ramping, remains a significant pain point for most users due to the complexity of multi-step fund transfers, a lack of transparency and security regarding transactions, and redundant fees. While some players in the market are aiming to solve these problems at the expense of others, we have not yet seen a holistic approach. We aim to address these issues by providing blockchain wallet providers with a protocol and a hybrid Software Development Kit (SDK) that facilitates instant, transparent, and fully-compliant card payments backed by non-custodial wallets.*

### Introduction

Historically, centralised exchanges (CEXes) have provided acceptable user experiences across their product range due to the custodial nature of their offerings: most crypto-related transactions within their ecosystems remain unreflected on actual blockchain networks, existing only on internal ledgers. While many users find this acceptable, security concerns and CEX reliability issues have driven many users towards crypto-native fields dominated by on-chain products, most notably Decentralised Finance (DeFi) protocols.

At the time of writing, DeFi on-chain products have mostly reached feature parity with products built by CEXes [\[1\]](#references), and the associated fees are comparable. However, a significant gap remains in the form of reintroducing the value back into traditional fiat markets, or crypto off-ramping.

### Problem statement

Crypto-native users of non-custodial wallets struggle to off-ramp their assets easily, securely, and efficiently. Key issues include:

* **Complexity:** The process is multi-layered and involves numerous transactions across various applications and websites.
* **Third-party custody risk:** Users are required to transfer their funds into a custodial wallet to off-ramp crypto assets, at which point they lose visibility of their transactions.
* **High cost:** Off-ramping solutions levy high processing fees on transactions, partially to cover their potential losses due to fraud or compliance issues. At the same time, transactions are processed on expensive networks with little optimisation.

While attempts have been made to address these issues with native on-chain products, achieving this involves complexities:

* Blockchain networks are designed to prevent external parties from initiating transactions from a user’s wallet without prior approval. The near-instant nature of card transactions doesn’t allow for real-time consent retrieval.
* Blockchain transactions are atomic and final, which doesn’t align with traditional financial market transactions. Card authorisations can be revoked, adjusted, or may take significant time to settle.
* Financial transactions necessitate extensive monitoring, including Know-Your-Customer (KYC) procedures, active compliance checks, and security certifications for user data handling.

### Solution and architecture

We offer a comprehensive hybrid SDK, comprised of both on- and off-chain APIs. This SDK allows wallet application developers to provide users with a secure method to order payment cards and make payments backed by their crypto-assets stored in multi-chain wallets.

Our solution maintains the highest standards of blockchain network security while ensuring full transparency and compliance with financial regulations. This is achieved with the help of the following tools and concepts:

1. **Account Abstraction (AA):** Recent developments, most notably a proposal known as *ERC-4337* [\[2\]](#references), have paved the way for developers to provide a much simpler UX around transaction initiation and fund transfers. We clearly and transparently request users’ conditional consent to initiate payments on their behalf, placing smart contracts in charge of the process. At the same time, associated transaction fees can be bundled and relayed to designated paymasters improving cost efficiency and UX.
2. **Escrow Smart Contracts** [**\[3\]**](#references)**:** Once a payment authorisation has been approved and negotiated between a merchant and a card issuer, the user’s funds are temporarily locked in a contract until the underlying transaction is settled, revoked, or times out. This ensures full interoperability between the atomic nature of on-chain transactions and multi-state payments off-chain.
3. **Real-time Transaction Monitoring:** Our systems are constantly monitoring transactions related to users’ accounts validating them against fraud and compliance checks provided by our proprietary stack and trusted third-party solutions. Any suspicious on-chain activity is automatically detected and user’s access to our services can be restricted in order to ensure compliance with regulatory regimes.
4. **Off- and Cross-Chain Oracles** [**\[4\]**](#references) **and APIs:** An ensemble of oracle contracts ensures a consistent flow of funds between blockchain and payment networks maintaining transparency and interoperability. The complexity of intra-network communication is abstracted away, so developers can focus on what they do best: building products.

#### Card Onboarding

<div data-full-width="true"><figure><img src="https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FptM7QVqnwpcW5rCxRCWv%2Fcard-add.png?alt=media&amp;token=f4f0f4b0-05e5-43e6-bc3a-39bf07764c90" alt=""><figcaption><p>Figure 1: Card onboarding flow diagram</p></figcaption></figure></div>

In this flow (Figure 1), the user interacts with our infrastructure via an application SDK integrated into the wallet application. During onboarding, we request KYC and other compliance-related information required by our partners to open a new card account for the user. Upon successful completion, we ask the user to set up optional account preferences such as spending limits and token order. The fully configured card account is deployed by the user to the blockchain network as an ERC-4337 [\[2\]](#references) compatible smart contract. Finally, we finalise the card issuance process and mint a new NFT representing the card to the user’s wallet for later recognition by our systems.

#### Card Payment

<div data-full-width="true"><figure><img src="https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FYbX9uGA6h5nvuty1KSKW%2Fcard-payment.png?alt=media&amp;token=3526e8a2-f434-4bb6-a06b-d11e2e4345cf" alt=""><figcaption><p>Figure 2: Card payment flow diagram</p></figcaption></figure></div>

After the card account setup, the user can start making card payments (Figure 2). When our systems receive a payment authorisation request from the payment network (Visa, MasterCard, etc.), we validate whether the user has a sufficient spend- able balance. If they do, we approve the request and simultaneously initiate an on-chain transaction, transferring the required amount of eligible tokens into a escrow contract.

Periodically, we receive status updates from the payment network about initiated transactions. To ensure full transparency, we defer the verification stage and token release procedures to the escrow smart contract [\[3\]](#references) utilising oracles [\[4\]](#references). Once the contract confirms that the transaction is completed, tokens are released and transferred to the settlement wallet for further off-ramp and settlement with the merchant bank. In the case of a transaction reversal or timeout, the user’s funds are released and transferred back to their wallet.

### Key benefits

By utilising the latest developments in blockchain networks and pairing them with fintech solutions provided by our proprietary stack and trusted partners, we offer the best of both worlds: a familiar neobank-style user experience around traditional banking products with the security and transparency of blockchain technologies.

Our hybrid SDK provides a simple integration process for application developers, allowing them to issue card products and offer payment initiation services to their customers in no time and benefit from secure, trustless access to major payment networks globally.

While account abstraction allows us to manage user on-chain funds effectively with their prior consent, the user remains in full control of their assets as they can modify or even revoke their consent at any time. Meanwhile, our staging contract model offers the highest level of transparency on asset transactions until we settle a completed transaction.

Despite the requirement for multiple token transfers before funds are settled, utilising account abstraction allows us to optimise cost by bundling multiple transactions and performing all operations automatically and transparently for the user.

### Product roadmap

<table data-header-hidden><thead><tr><th width="117"></th><th></th></tr></thead><tbody><tr><td><strong>Q3 2023</strong></td><td><p><strong>Proof-of-concept (PoC) Testing and Pre-launch</strong></p><ul><li>Implement crucial building blocks required for end-to-end operations: user onboarding, card issuance, crypto off-ramping, on-chain contracts on one EVM-compatible chain (eg Polygon or Optimism)</li><li>Launch our own card payments system with a limited target group (around 200 users) in order to validate and test the product</li></ul></td></tr><tr><td><strong>Q4 2023</strong></td><td><p><strong>Preparation for Public Launch and Pilot Partnership</strong></p><ul><li>Perform full audit of smart contracts</li><li>Bundle implemented APIs and components in a form of fully re-usable white-labeled SDK</li><li>Launch pilot partnership and integration with a wallet provider</li></ul></td></tr><tr><td><strong>Q1 2024</strong></td><td><p><strong>Expansion to Major EVM Chains and Public Launch</strong></p><ul><li>Deploy smart contracts to major chains such as Avalanche, Arbitrum and zkSync</li><li>Launch our own standalone product publicly in order to validate operations at scale</li><li>Prepare for wider launch with wallet providers and web3 developers</li></ul></td></tr><tr><td><strong>Q2 2024</strong></td><td><p><strong>Full Scale Partnership Launch and Product Expansion</strong></p><ul><li>Make our SDKs available for wider partnership integrations with developers</li><li><p>Explore possibilities to expand product offering by providing instant swaps</p><p>on token liquidation and other DeFi features</p></li><li>Explore potential expansion to offering more embedded finance products such as managed self-custody bank accounts</li></ul></td></tr></tbody></table>

### Conclusion

Our trustless payment solution is a necessary step towards the mass adoption of web3 products. By creating a bridge between the worlds of blockchain and traditional fiat payment networks, we enable users to enjoy the benefits of decentralised products on-chain, while still maintaining fully compliant and verified pathways to integrate their assets back into traditional markets. The immediacy and security of these transactions significantly enhance the user experience.

Although launching a standalone retail solution for crypto-backed card payments is a viable strategy, we believe a customisable, packaged hybrid SDK will facilitate greater user reach and adoption. By simplifying integration for wallet providers and web3 developers, we can ensure our solution is easily accessible to a large pool of potential users, which, in turn, benefits the industry as a whole.

### References

1. *Andrea Barbon and Angelo Ranaldo. On the quality of cryptocurrency markets: Centralized versus decentralized exchanges.* [*https://ssrn.com/abstract=3984897*](https://ssrn.com/abstract=3984897)*, 7 2023.*
2. *Vitalik Buterin et al. Erc-4337: Account abstraction using alt mempool.* [*https://eips.ethereum.org/EIPS/eip-4337*](https://eips.ethereum.org/EIPS/eip-4337)*, 9 2021.*
3. *Steven Goldfeder, Joseph Bonneau, Rosario Gennaro, and Arvind Narayanan. Escrow Protocols for Cryptocurrencies: How to Buy Physical Goods Using Bitcoin, pages 321–339. 1 2017.*
4. *Abdeljalil Beniiche. A study of blockchain oracles. ArXiv, abs/2004.07140, 2020.*


# Supported Blockchains and Assets

OffBlocks is built in a chain-agnostic way and we're striving to support all major blockchains with smart contract and account abstraction support. Nevertheless, we are adding support for new blockchains in a staged approach with major EVM-based blockchains leading the way.

When it comes to assets available to fund payments, we are striving to support as many tokens as we can, with initial emphasis being on natively supported stablecoins such as USDC and EURC.

Please, refer to the following tables for latest status on all supported blockchains and assets.

### Testnets

<table data-full-width="true"><thead><tr><th>Name</th><th>ID</th><th>Status</th><th data-hidden data-type="checkbox"></th></tr></thead><tbody><tr><td>zkSync Sepolia</td><td>eip155:300</td><td>Building in Sandbox</td><td>true</td></tr><tr><td>Arbitrum Sepolia</td><td>eip155:421614</td><td>Building in Sandbox</td><td>true</td></tr><tr><td>Polygon PoS Mumbai</td><td>eip155:80001</td><td>Building in Sandbox</td><td>true</td></tr></tbody></table>

### Testnet Assets

<table data-full-width="true"><thead><tr><th width="126">Name</th><th width="192">Blockchain</th><th width="443">ID</th><th>Status</th><th data-hidden data-type="checkbox"></th></tr></thead><tbody><tr><td>USD Coin</td><td>Arbitrum Sepolia</td><td>epi155:421614/erc20:0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d</td><td>Building in Sandbox</td><td>true</td></tr><tr><td>USD Coin</td><td>Polygon PoS Mumbai</td><td>eip155:80001/erc20:0x9999f7fea5938fd3b1e26a12c3f2fb024e194f97</td><td>Building in Sandbox</td><td>true</td></tr></tbody></table>


# FAQ

### I'm exploring embedded banking solutions to offer my users easy off-ramping and instant payments, why should I choose OffBlocks?

OffBlocks offers more than just an embedded banking solution. We have developed a protocol and infrastructure that enables complete interoperability between on-chain solutions and traditional payment networks. By choosing OffBlocks, you empower your users to instantly establish smart accounts on their preferred blockchain network. These smart accounts can seamlessly settle transactions on traditional payment networks like Visa and MasterCard, ensuring the utmost security and transparency. Our systems handle the entire lifecycle of these accounts, including identity verification, account creation, compliance monitoring, and fund transfers.

### What exactly is a Smart Account?

A Smart Account is essentially a Smart Contract that holds a user's assets. It fully supports Account Abstraction, as introduced on EVM-compatible networks with ERC-4337. This means it enables smart operations like delegated transactions, spending limits, gas sponsorship, transaction batching, and more.

### Are Smart Accounts fully non-custodial?

There's a common misconception that AA accounts are inherently non-custodial. Ultimately, custody of the account depends on who owns the private key. Therefore, Smart Accounts are neutral regarding custody requirements. You can choose to create Smart Accounts on behalf of your users, making them custodial, or allow your users to create and configure their accounts independently. Either way, Smart Accounts function entirely on a decentralised ledger provided by a blockchain network.

### Don't users need to approve every transaction? How does transaction delegation work?

Prior to ERC-4337, users had to explicitly initiate every transaction by signing it with their private key. Account abstraction separates private key wallets from accounts. While users still need to authorise account creation and basic setup, they can delegate certain actions, such as fund transfers, to an authorised party using consent keys. A consent key empowers an account manager to perform specific actions within authorized limits. For instance, a user can set up their smart account to automatically approve card payments up to a predetermined spending limit each month, eliminating the need to authorise every transaction manually. Going beyond Ethereum ecosystem, many blockchain networks support account abstraction natively, which makes consents and delegated transactions work even smoother.

### What is Gas Sponsorship?

Before the adoption of ERC-4337, every transaction authorized by a wallet owner incurred a gas fee in the network's native token (e.g., ETH or MATIC). This meant that a user's wallet always needed some amount of native token, even for transactions involving ERC-20 token transfers. Account Abstraction introduced the concept of Paymasters, designated accounts capable of sponsoring gas fees on behalf of users. By setting up a Paymaster with OffBlocks, you can enhance your users' experience by eliminating the need for them to hold any native tokens in their account. This either eliminates gas fees or allows transaction fees to be funded with ERC-20 tokens.

### Will users be able to use any tokens to fund their payment transactions?

Initially, we will support only a limited selection of stablecoin tokens for instant payments. As our system matures, we will progressively enable more tokens, including liquid ERC-20 tokens and native tokens like ETH, MATIC, SOL and TRX, for instant transaction funding.

### What happens when a user makes a payment with their card?

First and foremost, we verify whether the user's Smart Account linked to the card holds sufficient value to cover the transaction and is within pre-authorised spending limits. If these checks are successful, the token value of the transaction is locked in an Escrow Smart Contract. This specialised smart contract temporarily locks funds until all conditions for transaction completion are met or until the transaction is reverted or times out. This ensures that the user's funds are not prematurely transferred to a merchant if they choose to cancel the transaction. Once the merchant confirms the transaction, we release the user's tokens from escrow and transfer them to a settlement account for subsequent off-ramping and settlement with the bank.

### What happens if transaction is not confirmed by the merchant?

Merchants typically have between 2 to 7 days to confirm or capture a transaction. If, for any reason, they choose not to do so within the specified timeframe or explicitly cancel or void the authorisation, the escrow smart contract will automatically return the funds to the user's Smart Account.

### How do you handle foreign currency transactions?

Each card issued through OffBlocks has a native currency, such as EUR or GBP, based on the user's residency and our agreements with payment networks. Initially, we will rely on foreign exchange services provided by payment networks like Visa or MasterCard. These services offer competitive FX rates while streamlining settlement procedures in a single native currency. Initially, we will support only selected tokens pegged to respective currencies. In the future, we plan to expand this solution with automated on-chain swaps, allowing users to spend liquid tokens while saving on FX rates and fees.


# System Architecture

### Overview

Our system architecture seamlessly combines both on-chain and off-chain components to deliver optimal performance and an exceptional user experience while maintaining a robust level of security for our users. For detailed insights into how various components within our system interact in different workflows, please refer to our comprehensive [Whitepaper](/overview/whitepaper).

### Off-chain Services and Components

Our off-chain services are meticulously designed to seamlessly integrate with payment networks, KYC (Know Your Customer) providers, and compliance tools. These services also play a crucial role in providing real-time transaction data to our APIs and orchestrating smart contracts deployed on a variety of blockchain networks supported by our products.

<div data-full-width="true"><figure><img src="https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FDlMEjvuc0tZQtidoIwzs%2Farchitecture.png?alt=media&amp;token=501ae204-760a-486b-ad4e-315d2bc22847" alt=""><figcaption></figcaption></figure></div>

#### Blockchain Nodes

We maintain a network of dedicated services that function as nodes on various blockchain networks. These nodes enable us to obtain a live feed of all transactions conducted by our users. This invaluable capability allows us to perform real-time analysis, conducting fraud detection and compliance checks. Additionally, it ensures that our internal systems and ledgers remain in perfect synchronization with the states of the respective blockchains.

#### Oracles

Our system incorporates both cross-chain and off-chain oracles. These oracles serve a critical role in providing our smart contracts with access to essential off-chain data. This includes information such as the status of user verification processes, the state of payment network transactions, and account data stored off-chain.

#### Partner Integrations

We rely on trusted partners to facilitate various checks and validations, as well as to establish connections with payment networks. The services in this section are dedicated to managing and maintaining these crucial integrations, ensuring the seamless flow of data and transactions within our ecosystem.

## Smart Contracts

The on-chain segment of our system is deployed in the form of smart contracts across the chains we support, encompassing both test and mainnet environments. These smart contracts include ERC-4337 compatible account contracts, empowering users to provide conditional consent for our services to manage their funds and execute transactions on their behalf on EVM-compatible networks. Additionally, escrow contracts are utilised to guarantee full interoperability between blockchain transactions and transactions on payment networks.


# API Integration

{% hint style="warning" %}
**Coming soon**

This section is under constant development and so is our API. Please, check back later or reach out to <gm@offblocks.xyz> for more details
{% endhint %}

## Overview

Sometimes you want to be in a full control over the way you integrate with your partners and we totally understand that. As we are continuously reviewing our API in order to let our partners to provide the best user experience to their customers, API integration route remains highly experimental at the moment and we encourage you to reach out to us in order to discuss the best options moving forward.


# Getting Started

Welcome to OffBlocks API, a powerful platform that offers comprehensive endpoints and capabilities for user (customer) onboarding, account and card management, transaction lifecycle, and real-time updates via webhooks. This guide provides step-by-step instructions on how to seamlessly integrate your application with OffBlocks API. For your reference and code generation, you can also download the full OpenAPI 3.1 specification for OffBlocks API [here](https://offblocks.github.io/offblocks-api-spec/).

## Environments

OffBlocks API is currently available in a limited Sandbox environment, offering reduced functionality for demonstration purposes. As our API evolves, we will progressively enable more features and functionality through the Production environment.

The current version of the Sandbox API can be accessed via the following URL: `https://api.sandbox.offblocks.xyz/v1`. To set up your access and begin exploring the capabilities, please contact us at <gm@offblocks.xyz>.

## Key Concepts

{% content-ref url="/pages/hY2gzP4v0f7bvlCat77c" %}
[Postman](/developer-guides/api-integration/postman)
{% endcontent-ref %}

{% content-ref url="/pages/eNhBlm8vmEaxytbYrSKB" %}
[Authentication](/developer-guides/api-integration/authentication)
{% endcontent-ref %}

{% content-ref url="/pages/ThLReV5Qw61rF3djP0r9" %}
[Request Signatures](/developer-guides/api-integration/request-signatures)
{% endcontent-ref %}

{% content-ref url="/pages/pxsqRMSccgAjcOpa4yM5" %}
[Idempotency](/developer-guides/api-integration/idempotency)
{% endcontent-ref %}

{% content-ref url="/pages/mi0j6evUbiiDHZd5eWVT" %}
[Blockchain Identifiers](/developer-guides/api-integration/blockchain-identifiers)
{% endcontent-ref %}

{% content-ref url="/pages/h5ZZLhLrdVc3RJSwcIwl" %}
[Blockchain Updates](/developer-guides/api-integration/blockchain-updates)
{% endcontent-ref %}

{% content-ref url="/pages/QqG4jhe9WCoJyQngZC2A" %}
[Sign in With X](/developer-guides/api-integration/sign-in-with-x)
{% endcontent-ref %}

{% content-ref url="/pages/WK7vLCbYyq8RdjkjyLrk" %}
[Fees](/developer-guides/api-integration/fees)
{% endcontent-ref %}

{% content-ref url="/pages/p6DNcw72Y9PvhZskhmuT" %}
[Simulator](/developer-guides/api-integration/simulator)
{% endcontent-ref %}

## API Documentation

{% content-ref url="/pages/unRGR6fBilhhfVFe1D67" %}
[API Reference](/developer-guides/api-integration/api-reference)
{% endcontent-ref %}

{% content-ref url="/pages/CuuSi4EGo7jNbRDxNk2M" %}
[States and Transitions](/developer-guides/api-integration/states-and-transitions)
{% endcontent-ref %}

{% content-ref url="/pages/8AH8qiSOZI4VqMEliXVb" %}
[Step-by-step Guide](/developer-guides/step-by-step-guide)
{% endcontent-ref %}


# Postman

If you are using [Postman](https://www.postman.com/), you can fork our Postman collection

[![Run In Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/31088643-2c2b6d50-87d1-47fc-8ab7-47ea53ff5cb9?action=collection%2Ffork\&source=rip_markdown\&collection-url=entityId%3D31088643-2c2b6d50-87d1-47fc-8ab7-47ea53ff5cb9%26entityType%3Dcollection%26workspaceId%3D556db888-8f56-4283-bcd3-46a482cfc476#?env%5BSANDBOX%5D=W3sia2V5IjoiYmFzZVVybCIsInZhbHVlIjoiaHR0cHM6Ly9hcGkuc2FuZGJveC5vZmZibG9ja3MueHl6L3YxIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQiLCJzZXNzaW9uVmFsdWUiOiJodHRwczovL2FwaS5zYW5kYm94Lm9mZmJsb2Nrcy54eXovdjEiLCJzZXNzaW9uSW5kZXgiOjB9LHsia2V5IjoiYXBpS2V5IiwidmFsdWUiOiIiLCJlbmFibGVkIjp0cnVlLCJ0eXBlIjoic2VjcmV0Iiwic2Vzc2lvblZhbHVlIjoiIiwic2Vzc2lvbkluZGV4IjoxfSx7ImtleSI6ImFwaVNlY3JldCIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6InNlY3JldCIsInNlc3Npb25WYWx1ZSI6IiIsInNlc3Npb25JbmRleCI6Mn0seyJrZXkiOiJiZWFyZXJUb2tlbiIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImFueSIsInNlc3Npb25WYWx1ZSI6IiIsInNlc3Npb25JbmRleCI6M30seyJrZXkiOiJzaWduYXR1cmVLZXlJZCIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6InNlY3JldCIsInNlc3Npb25WYWx1ZSI6IiIsInNlc3Npb25JbmRleCI6NH0seyJrZXkiOiJzaWduYXR1cmVQcml2YXRlS2V5IiwidmFsdWUiOiIiLCJlbmFibGVkIjp0cnVlLCJ0eXBlIjoic2VjcmV0Iiwic2Vzc2lvblZhbHVlIjoiIiwic2Vzc2lvbkluZGV4Ijo1fSx7ImtleSI6InNpZ25hdHVyZVB1YmxpY0tleSIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQiLCJzZXNzaW9uVmFsdWUiOiIiLCJzZXNzaW9uSW5kZXgiOjZ9XQ==)

This collection includes:

* OffBlocks API endpoints
* An environment to fill out
* A pre-request script that handles [Authentication](/developer-guides/api-integration/authentication) and [Request Signatures](/developer-guides/api-integration/request-signatures) for you for every request

### **Setup**

To make calls within Postman you need to:

<details>

<summary>1. Create a public / private key pair for request signing</summary>

To learn more, see [Request Signatures](/developer-guides/api-integration/request-signatures)&#x20;

```bash
openssl ecparam -name secp384r1 -genkey -noout -out ec-secp384r1-priv-key.pem
openssl ec -in ec-secp384r1-priv-key.pem -pubout > ec-secp384r1-pub-key.pem
```

</details>

<details>

<summary>2. Request API credentials</summary>

To set up your access and begin exploring the capabilities, please contact us at <gm@offblocks.xyz>.

</details>

<details>

<summary>3. Populate the environment in Postman</summary>

On the left pane, you should see "Environments". If you go there, there is one Postman Environment available.

Fill out the values in there (fill the **`Current Value`** column) with the values shown in the section below.

When you're done, set this environment as "Active" (meaning it will be used when you try endpoints) by clicking on the checkmark icon next to the environment name, or by selecting it in the dropdown selector in the top-right corner of Postman window.

* **baseUrl:** Use `https://api.sandbox.offblocks.xyz/v1` to access the Sandbox environment.
* **apiKey:** Please reach out to us so that we can provide you with the necessary API credentials.
* **apiSecret:** Similarly, contact us to obtain your API credentials.
* **signatureKeyId:** This unique identifier (UUID) is used to verify the authenticity of your requests. (see [Request Signatures](/developer-guides/api-integration/request-signatures) for more details).
* **signaturePrivateKey:** This private key is used to sign all outgoing requests, ensuring their authenticity. (See [Request Signatures](/developer-guides/api-integration/request-signatures) for more details).

</details>


# Authentication

The OffBlocks API utilises the Bearer authentication mechanism. Before accessing any of the authenticated endpoints, you must obtain an authentication token by providing your client credentials: `apiKey` and `apiSecret`. This process is automatically handled for all requests in our Postman collection.

### Retrieving an Authentication Token

The endpoint mentioned above returns a JSON Web Token (JWT) token. You must include this token as a Bearer token in the header of all requests that require authentication. Additionally, the response includes an expiration timestamp for the token, measured in seconds.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/token" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Changing Stored Credentials

Should you suspect that your credentials have been compromised, you can update them using the following endpoint:

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/credentials" method="put" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

This endpoint allows you to replace your current `apiKey` and `apiSecret` with new, more secure credentials.

### Removing API Credentials

If necessary, you can completely remove your API credentials by using the following endpoint:

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/credentials" method="delete" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Please exercise caution when using this endpoint, as it will permanently revoke your access to the OffBlocks API.


# Request Signatures

### Signing Your Requests

For every authenticated request made to the OffBlocks API, it is essential to sign the request using an HTTP signature following a [pending IETF specification](https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-19.html). Various open-source libraries are available to facilitate the generation of valid signatures. We are also actively working on publishing our own libraries to assist developers. For more information, please do not hesitate to contact us.

We actively develop a [Go library](https://pkg.go.dev/github.com/offblocks/httpsig) to support HTTP message signatures as well as contribute to a [Node.js library](https://www.npmjs.com/package/http-message-signatures). Reach out to us if you're using other technologies on your backend and we'll be happy to assist with any issues integrating HTTP message signatures.

### Supported Algorithms

While our API can theoretically accommodate multiple cryptographic algorithms for request signing, for the time being we only support [ECDSA with curve P-384 DSS and SHA-384](https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html#name-ecdsa-using-curve-p-384-dss) as a signing algorithm.

### Key Generation

{% hint style="info" %}
In this guide we use `openssl` as a key generation tool. Feel free to use a tool of your choice, just make sure you generate keys using correct algorithm
{% endhint %}

Creating an ECDSA key pair for accessing our API is a straightforward process that involves just two commands:

1. Create a private key.

   ```bash
   openssl ecparam -name secp384r1 -genkey -noout -out ec-secp384r1-priv-key.pem
   ```

   Sample contents of the `ec-secp384r1-priv-key.pem` private key in PEM format:

   ```
   -----BEGIN EC PRIVATE KEY-----
   MIGkAgEBBDB8VIl4tFsxpD/7QN7LNWqzWhBrksKY1gdbp6aFzN5kBM5+PtDL/C1Y
   nF+ACeMbf7ygBwYFK4EEACKhZANiAAQ1WCxX6Z7z/bbj6PntmWChGqtzfX5mmLLU
   +621jNM55fI2k5sPvPUmd891mU/L7FAPxowiE8Cu48hHXTayYqDaO9CE0m8eWWXg
   T6s+j8ku4qgs66a9GDh3IG3tjelZzVY=
   -----END EC PRIVATE KEY-----
   ```
2. Create a public key by extracting it from the private key.

   ```bash
   openssl ec -in ec-secp384r1-priv-key.pem -pubout > ec-secp384r1-pub-key.pem
   ```

   Sample contents of the `ec-secp384r1-pub-key.pem` public key in PEM format:

   ```
   -----BEGIN PUBLIC KEY-----
   MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAENVgsV+me8/224+j57ZlgoRqrc31+Zpiy
   1PuttYzTOeXyNpObD7z1JnfPdZlPy+xQD8aMIhPAruPIR102smKg2jvQhNJvHlll
   4E+rPo/JLuKoLOumvRg4dyBt7Y3pWc1W
   -----END PUBLIC KEY-----
   ```

### Registering your Public Key with the API

To enable our API to recognise your signatures, you need to upload the public key using the `/auth/signing` `PUT` endpoint. Please ensure that you set all the required environment variables (see [Getting Started](/developer-guides/api-integration/getting-started)) in your Postman workspace before using this endpoint.

{% hint style="warning" %}
Make sure you encode your public key using `base64` encoding before sending through our endpoint, otherwise the request will fail
{% endhint %}

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/signing" method="put" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Additionally, previously uploaded keys can be updated using the same endpoint or permanently removed using a `DELETE` request to `/auth/signing`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/signing" method="delete" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Request Content Digest

While it is important to verify signature of the request itself, it is equally important to verify integrity of request body if it is provided. For this purpose we are using content digest header containing hash of request body calculated using pre-defined algorithm. The header is required for all API requests that are expected to have body, either `POST` or `PUT`. In line with [HTTP Digest Header specification](https://www.ietf.org/archive/id/draft-ietf-httpbis-digest-headers-13.html), we are verifying `Content-Digest` header with request body hash calculated using `sha-256` and/or `sha-512` algorithms. It is also strongly advised to include `Content-Digest` header as part of signature verification in order to verify authenticity of the entire request.

{% hint style="info" %}
While developers are also encouraged to use canonical JSON representation of request bodies to ensure stable hash calculation, we have made a decision to simply verify digest of byte array representation of the body ***as is***. Please, make sure that request body is not transformed in any way after digest has been calculated or otherwise request verification may fail
{% endhint %}


# Idempotency

The OffBlocks API supports API idempotency. This allows safe request retries (during the validity time window of the key) while only performing the requested action once.

For example, if there is a temporary connection issue, a request to create an account could be safely retried several times without creating multiple accounts.

{% hint style="info" %}
Always use a different idempotency key for different requests

You **must** use different idempotency keys for different requests. Reusing the same idempotency key will cause the subsequent requests to not be executed. Reuse of an idempotency key with a different payload, while the key is still valid, results in an error.
{% endhint %}

### Enabling Idempotency

To use idempotency, where required (this includes all modifying requests such as `POST`, `PUT`, `PATCH` and `DELETE`), add the `Idempotency-Key` header as shown in the following example endpoint. We expect a key in a format of UUID to be provided. This is automatically generated and populated for relevant requests in our Postman collection.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Scope and Validity

Each idempotency key has a duration of **1 hour**.

During the validity period, you can only send the same payload with the same idempotency key. If you do this, you receive an identical response.

If the payload is different, but the idempotency key is the same, you receive an error. The error is likely 422 or 409, explained below.

### Error Handling

These are some of the common errors associated with idempotency:

| Error code | Error type                           | Detail                                                                   | Solution                                                                                                                                                                                                                                                                                                               |
| ---------- | ------------------------------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400        | BadRequest                           | Invalid request signature.                                               | Include the `Idempotency-Key` header when making requests to endpoints where idempotency is mandatory.                                                                                                                                                                                                                 |
| 409        | Idempotency-Key Concurrency Conflict | The Idempotency-Key value is being used for a concurrent request.        | <p>You submit a new request with a different idempotency key.<br><br>This error occurs because another request with the same idempotency key is in flight.<br><br>If you wait until the request is no longer in flight and make the same request with the same idempotency key, you receive an identical response.</p> |
| 422        | Idempotency-Key Reuse                | The Idempotency-Key value has already been used for a different request. | <p>Make your request with a new idempotency key.<br><br>Technically, you could also wait for the validity period of the idempotency key to end after 1 hours.</p>                                                                                                                                                      |


# Blockchain Identifiers

The OffBlocks API is chain-agnostic, meaning that our services can theoretically be integrated with a variety of different blockchains that support smart-contract functionality. This includes, but is not limited to, EVM-compatible L1 and L2 chains, Solana, and Tron.

To effectively set up entities such as users and accounts, perform compliance monitoring, and manage the transaction lifecycle, we employ universal identifiers for blockchain entities. We have chosen a CAIP-compatible ([Chain Agnostic Improvement Proposal](https://chainagnostic.org/)) naming convention for [Chain](https://chainagnostic.org/CAIPs/caip-2), [Account](https://chainagnostic.org/CAIPs/caip-10) and [Asset](https://chainagnostic.org/CAIPs/caip-19) identifiers, our internal Transaction identifier is also using [Account](https://chainagnostic.org/CAIPs/caip-10)-like structure for consistency.

### Chain Identifier

We use a blockchain namespace identifier suggested by [CAIP-2](https://chainagnostic.org/CAIPs/caip-2). Here are some examples of what it would look like for supported chains:

{% tabs %}
{% tab title="zkSync Era" %}

```json
// zkSync Era Mainnet
eip155:324

// zkSync Sepolia Testnet
eip155:300
```

{% endtab %}

{% tab title="Arbitrum" %}

```json
// Arbitrum One
eip155:42161

// Arbitrum Sepolia Testnet
eip155:421614
```

{% endtab %}

{% tab title="Polygon" %}

```json
// Polygon Mainnet
eip155:137

// Polygon Mumbai Testnet
eip155:80001
```

{% endtab %}

{% tab title="StarkNet" %}

```json
// StarkNet Mainnet
starknet:SN_MAIN

// StarkNet Sepolia
starknet:SN_SEPOLIA
```

{% endtab %}

{% tab title="Solana" %}

```json
// Solana Mainnet
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp

// Solana Devnet
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1

// Solana Testnet
solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z
```

{% endtab %}

{% tab title="Tron" %}

```json
// Tron Mainnet
tron:27Lqcw

// Tron Shasta Testnet
tron:4oPwXB

// Tron Nile Testnet
tron:6FhfKq
```

{% endtab %}
{% endtabs %}

### Account Identifier

We use a blockchain account identifier suggested by [CAIP-10](https://chainagnostic.org/CAIPs/caip-10). Here are some examples of what it would look like for supported chains:

{% tabs %}
{% tab title="zkSync Era" %}

```json
// zkSync Era Mainnet
eip155:324:0x22227A31dd842196A246d8f3b775998560eAa61d

// zkSync Sepolia Testnet
eip155:300:0x22227A31dd842196A246d8f3b775998560eAa61d
```

{% endtab %}

{% tab title="Arbitrum" %}

```json
// Arbitrum One
eip155:42161:0x22227A31dd842196A246d8f3b775998560eAa61d

// Arbitrum Sepolia Testnet
eip155:421614:0x22227A31dd842196A246d8f3b775998560eAa61d
```

{% endtab %}

{% tab title="Polygon" %}

```json
// Polygon Mainnet
eip155:137:0x22227A31dd842196A246d8f3b775998560eAa61d

// Polygon Mumbai Testnet
eip155:80001:0x22227A31dd842196A246d8f3b775998560eAa61d
```

{% endtab %}

{% tab title="StarkNet" %}

```json
// StarkNet Mainnet
starknet:SN_MAIN:0x02DdfB499765c064eaC5039E3841AA5f382E73B598097a40073BD8B48170Ab57

// StarkNet Sepolia
starknet:SN_SEPOLIA:0x02DdfB499765c064eaC5039E3841AA5f382E73B598097a40073BD8B48170Ab57
```

{% endtab %}

{% tab title="Solana" %}

```json
// Solana Mainnet
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv

// Solana Devnet
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1:DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK

// Solana Testnet
solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z:6LmSRCiu3z6NCSpF19oz1pHXkYkN4jWbj9K1nVELpDkT
```

{% endtab %}

{% tab title="Tron" %}

```json
// Tron Mainnet
tron:27Lqcw:TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL

// Tron Shasta Testnet
tron:4oPwXB:TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL

// Tron Nile Testnet
tron:6FhfKq:TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL
```

{% endtab %}
{% endtabs %}

### Transaction Identifier

We use a blockchain transaction identifier that is similar to the account identifier, as suggested by [CAIP-10](https://chainagnostic.org/CAIPs/caip-10), where the transaction hash replaces the account address. Here are some examples of what it would look like for supported chains:

{% tabs %}
{% tab title="zkSync Era" %}

```json
// zkSync Era Mainnet
eip155:324:0x4593e7f275383e76708718ead8f7226a804bea47fc9061ff649d105e943323e3

// zkSync Sepolia Testnet
eip155:300:0x4593e7f275383e76708718ead8f7226a804bea47fc9061ff649d105e943323e3
```

{% endtab %}

{% tab title="Arbitrum" %}

```json
// Arbitrum One
eip155:42161:0x4593e7f275383e76708718ead8f7226a804bea47fc9061ff649d105e943323e3

// Arbitrum Sepolia Testnet
eip155:421614:0x4593e7f275383e76708718ead8f7226a804bea47fc9061ff649d105e943323e3
```

{% endtab %}

{% tab title="Polygon" %}

```json
// Polygon Mainnet
eip155:137:0x4593e7f275383e76708718ead8f7226a804bea47fc9061ff649d105e943323e3

// Polygon Mumbai Testnet
eip155:80001:0x4593e7f275383e76708718ead8f7226a804bea47fc9061ff649d105e943323e3
```

{% endtab %}

{% tab title="StarkNet" %}

```json
// StarkNet Mainnet
starknet:SN_MAIN:0x03628d947d5aa8e2eeecbd49767ccea7ecce183b644f439bb8946a70576cff3c

// StarkNet Sepolia
starknet:SN_SEPOLIA:0x03628d947d5aa8e2eeecbd49767ccea7ecce183b644f439bb8946a70576cff3c
```

{% endtab %}

{% tab title="Solana" %}

```json
// Solana Mainnet
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:4agwj7yrJMc9K95v8zH1Bxg7vPPr5f5tN45Asu3asbjmc4iCaWDgQDWuYVvsrVzKkrJcB2ZXmbgToDvGX1kYgmz7

// Solana Devnet
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1:4agwj7yrJMc9K95v8zH1Bxg7vPPr5f5tN45Asu3asbjmc4iCaWDgQDWuYVvsrVzKkrJcB2ZXmbgToDvGX1kYgmz7

// Solana Testnet
solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z:4agwj7yrJMc9K95v8zH1Bxg7vPPr5f5tN45Asu3asbjmc4iCaWDgQDWuYVvsrVzKkrJcB2ZXmbgToDvGX1kYgmz7
```

{% endtab %}

{% tab title="Tron" %}

```json
// Tron Mainnet
tron:27Lqcw:7e1c4712c81b5e2b960883dc4beb78c26f3549e862c139aab88206eb4a577430

// Tron Shasta Testnet
tron:4oPwXB:7e1c4712c81b5e2b960883dc4beb78c26f3549e862c139aab88206eb4a577430

// Tron Nile Testnet
tron:6FhfKq:7e1c4712c81b5e2b960883dc4beb78c26f3549e862c139aab88206eb4a577430
```

{% endtab %}
{% endtabs %}

### Asset Identifier

We use a blockchain asset identifier suggested by [CAIP-19](https://chainagnostic.org/CAIPs/caip-19). Here are some examples of what it would look like for supported chains:

{% tabs %}
{% tab title="zkSync Era" %}

```json
// zkSync Era Mainnet
eip155:324/erc20:0x6b175474e89094c44da98b954eedeac495271d0f

// zkSync Sepolia Testnet
eip155:300/erc20:0x6b175474e89094c44da98b954eedeac495271d0f
```

{% endtab %}

{% tab title="Arbitrum" %}

```json
// Arbitrum One
eip155:42161/erc20:0x6b175474e89094c44da98b954eedeac495271d0f

// Arbitrum Sepolia Testnet
eip155:421614/erc20:0x6b175474e89094c44da98b954eedeac495271d0f
```

{% endtab %}

{% tab title="Polygon" %}

```json
// Polygon Mainnet
eip155:137/erc20:0x6b175474e89094c44da98b954eedeac495271d0f

// Polygon Mumbai Testnet
eip155:80001/erc20:0x6b175474e89094c44da98b954eedeac495271d0f
```

{% endtab %}

{% tab title="StarkNet" %}

```json
// StarkNet Mainnet
starknet:SN_MAIN/erc20:0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8

// StarkNet Sepolia
starknet:SN_SEPOLIA/erc20:0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8
```

{% endtab %}

{% tab title="Solana" %}

```json
// Solana Mainnet
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

// Solana Devnet
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

// Solana Testnet
solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
```

{% endtab %}

{% tab title="Tron" %}

```json
// Tron Mainnet
tron:27Lqcw/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t

// Tron Shasta Testnet
tron:4oPwXB/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t

// Tron Nile Testnet
tron:6FhfKq/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
```

{% endtab %}
{% endtabs %}


# Blockchain Updates

Our API includes various endpoints that involve on-chain activities and updates, such as ownership verification and smart contract deployment. In order to provide smooth experience and maximum level of security, we employ cryptographic challenges and signatures tailored to each supported blockchain.

## Account Creation

To create a new account for the customer and set it up for fund transfers with OffBlocks, we need to deploy a smart-contract account to the respective blockchain. The customer remains the ultimate owner of this account, and to manage funds for off-chain transactions on the user's behalf, we need them to sign a session key issued in the form of a cryptographic challenge. Similar to other endpoints, the challenge returned by the endpoint is a Base64-encoded string.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/accounts" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

To submit a signed challenge, please use the corresponding endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/challenge/sign" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

If, for any reason, the original challenge is lost or cannot be signed immediately, you can fetch it again using a dedicated endpoint. If we encounter an error during signature validation via `/sign` endpoint, a new challenge will be issued for you to retrieve.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/challenge" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Sign in With X

{% hint style="info" %}
This functionality is currently only supported on EVM networks and is 100% compliant with [EIP-4361 (Sign in With Ethereum)](https://eips.ethereum.org/EIPS/eip-4361). We will be adding support for more networks soon.
{% endhint %}

To verify a customer's externally-owned account ownership, we require a challenge to be signed with the private key. This mechanism is outlined in [CAIP-122](https://chainagnostic.org/CAIPs/caip-122), originally known as "Sign in With Ethereum" (SIWE) but expanded to support other blockchains. When a new customer object is created in our system, we generate a challenge following the standardised structure, expecting it to be signed and verified before proceeding with user onboarding. The challenge returned by the endpoint is a Base64-encoded string.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

When a new user is created on supported chains, you will receive a Base64 encoded challenge, which resembles the following:

```
api.sandbox.offblocks.xyz wants you to sign in with your Ethereum account:
0x43158f45b5EbD7b1179130130DF00393928C2691

OffBlocks wants to verify ownership of your wallet. By connecting your wallet and using OffBlocks, you agree to our Terms of services and Privacy Policy

URI: https://api.sandbox.offblocks.xyz
Version: 1
Chain ID: 300
Nonce: 5ufoiEqDaFvmfo78
Issued At: 2024-01-17T18:21:53Z
```

In order to proceed with ownership verification, your customer needs to sign this challenge with their private key. Please, follow [SIWE documentation](https://docs.login.xyz/) to learn more about signing SIWE challenges using community supported libraries. For testing purposes, you can use [MyCrypto](https://app.mycrypto.com/sign-message) signing tools to sign a challenge for test accounts.

If everything was successful, you will have a HEX-formatted signature ready to be submitted to our API. For example, if using MyCrypto, it will produce a following message for the above challenge:

```json
{
  "address": "0x43158f45b5EbD7b1179130130DF00393928C2691",
  "msg": "api.sandbox.offblocks.xyz wants you to sign in with your Ethereum account:\n0x43158f45b5EbD7b1179130130DF00393928C2691\n\nOffBlocks wants to verify ownership of your wallet. By connecting your wallet and using OffBlocks, you agree to our Terms of services and Privacy Policy\n\nURI: https://api.sandbox.offblocks.xyz\nVersion: 1\nChain ID: 300\nNonce: 5ufoiEqDaFvmfo78\nIssued At: 2024-01-17T18:21:53Z",
  "sig": "0xd2472857a11a29b612fda39b3a381cc88a8e6d085333cb7e2536ea38e71664be5e88ada8f5de966eff4f7c9cf59429b9a24814d878e779db34b612d4091c26ba1c",
  "version": "2"
}
```

To submit a signed challenge, please encode a HEX signature as Base64 string and send it using the corresponding endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/challenge/sign" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

If, for any reason, the original challenge is lost or cannot be signed immediately, you can fetch it again using a dedicated endpoint. If we encounter an error during signature validation via `/sign` endpoint, a new challenge will be issued for you to retrieve.&#x20;

We enforce a 1 hour expiration window via our API for all issued challenges for security purposes. If a challenge hasn't been signed within 1 hour from issue, we will generate a new one and make it available through this endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/challenge" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Fees

In order to ensure smooth operation of our services and transaction flow across blockchain and payment networks, we need to collect and pay fees. These include but are not limited to gas fees on blockchains, off-ramping fees and banking partner fees. Some of these fees are included in our monthly operations fees, however some of them are collected on a per-transaction basis and are subject to fluctuation due to various factors. You may choose to relay those fees entirely onto the customer, or sponsor certain fees. You may also choose to add your own monetisation stream by collecting transaction fees from your customers.

Every transaction contains a set of collected fees which are included as part of total settled amount. A fee object can be either of three types:

* `partner` - a fee collected based on your monetisation settings
* `service` - a fee paid to our service partners in order to facilitate the transaction. This may include any off-ramping or banking fees
* `network` - a gas fee collected by the blockchain to process all related transactions


# Simulator

Sometimes, you may want to test production flows in a sandbox without access to live payment networks or other service integrations. To facilitate that, we have introduced OffBlocks API Simulator, which simulates certain actions, such as incoming payment authorisations and customer verifications, for you to test API integration end-to-end.

&#x20;Please, refer to [Simulator](/developer-guides/api-integration/api-reference/simulator)page for more details on supported operations and API reference.


# API Reference

Full OpenAPI 3.1 specification for OffBlocks API is available to download for your reference and code generation [here](https://offblocks.github.io/offblocks-api-spec/).

{% content-ref url="/pages/Qc4NNXPIAvumQyMYDmlg" %}
[Customers](/developer-guides/api-integration/api-reference/customers)
{% endcontent-ref %}

{% content-ref url="/pages/gcPuwH3AfmMGWUpFGyQH" %}
[Accounts](/developer-guides/api-integration/api-reference/accounts)
{% endcontent-ref %}

{% content-ref url="/pages/ptHc5ywyogzp3EGA6OWc" %}
[Cards](/developer-guides/api-integration/api-reference/cards)
{% endcontent-ref %}

{% content-ref url="/pages/6ZvCEsOadf4Fv3mmD8Sx" %}
[Transactions](/developer-guides/api-integration/api-reference/transactions)
{% endcontent-ref %}

{% content-ref url="/pages/nP4J9u7MImpjhIBlCFJD" %}
[Webhooks](/developer-guides/api-integration/api-reference/webhooks)
{% endcontent-ref %}

{% content-ref url="/pages/a2KqCiIoQrXxjqMraJRm" %}
[Simulator](/developer-guides/api-integration/api-reference/simulator)
{% endcontent-ref %}


# Customers

API reference for customer-related endpoints

### Add a New Customer

To add a new customer to the system, use the authenticated `/customer` `POST` endpoint. This will initialise a new customer entity and start KYC verification process. The response contains a verification URL that redirects to our KYC partner's portal for further verification.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Customer Entity Updates

Any internal updates to the customer entity will trigger a webhook update event. For details on how to set up and manage webhook subscriptions, please refer to [Webhooks](/developer-guides/api-integration/api-reference/webhooks).

### Blockchain Updates

In order to proceed with customer verification, a challenge needs to be signed and verified (see [Blockchain Updates](/developer-guides/api-integration/blockchain-updates)).

### Retrieve Customer Entities

To retrieve a list of all customer entities registered for the programme, make a `GET` request to `/customers`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Retrieve an Individual Customer

To retrieve information about an individual customer, make a `GET` request to `/customers/{customerId}` with customer ID as a path parameter.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Deactivate a Customer

To deactivate a customer, use a `PATCH` request to `/customers/{customerId}/deactivate`. This will also deactivate all associated resources, such as accounts and cards.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/deactivate" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Reactivate a Customer

To reactivate a previously deactivated customer, use a `PATCH` request to `/customers/{customerId}/activate`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/activate" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Accounts

API reference for account-related endpoints

### Creating a New Account for a Customer

A new account for a customer can be created using the `POST` endpoint: `/customers/{customerId}/accounts`. .

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/accounts" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Account Entity Updates

Any internal updates to the account entity will be reported through webhook update events. For details on how to set up and manage webhook subscriptions, please refer to [Webhooks](/developer-guides/api-integration/api-reference/webhooks).

### Blockchain Updates

To proceed with account deployment and activation, a challenge issued by the endpoint must be signed and verified (see [Blockchain Updates](/developer-guides/api-integration/blockchain-updates)).

### Retrieve Account Entities

To retrieve a list of all account entities registered for the programme, make a `GET` request to `/accounts`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Retrieve an Individual Customer's Accounts

To retrieve information about an individual customer's accounts, make a `GET` request to `/customers/{customerId}/account` with the customer ID as a path parameter.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/accounts" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Retrieve an Individual Account

To retrieve information about an individual account, make a `GET` request to `/accounts/{accountId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Deactivate an Account

To deactivate an account, use a `PATCH` request to `/accounts/{accountId}/deactivate`. This action will also deactivate all associated resources, such as cards.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/deactivate" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Reactivate an Account

To reactivate a previously deactivated account, use a `PATCH` request to `/accounts/{accountId}/activate`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/activate" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Closing an Account

An account can be closed and subsequently removed from the programme using a `DELETE` request to `/account/{accountId}`. This will also deactivate all associated resources, including cards, and irreversibly deactivate the smart contract associated with it. All data will be preserved for audit and compliance purposes.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}" method="delete" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Cards

API reference for card-related endpoints

### Creating and Ordering a New Card for a Customer

A new card for the customer can be created and ordered using the `POST` endpoint: `/customers/{customerId}/cards`. This operation creates a new card through our issuance partner with Visa or MasterCard. If a physical card is requested, it also initiates a shipment order to the specified address. Please note that each card is linked to a specific account, and currently, we only support a limit of one virtual and one physical card per customer's account.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/cards" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Card Entity Updates

Any internal updates to the card entity will be reported through webhook update events. For details on how to set up and manage webhook subscriptions, please refer to [Webhooks](/developer-guides/api-integration/api-reference/webhooks).

### Retrieve Cards for a Specific Account

To retrieve a list of cards associated with a specific account, make a `GET` request to `/accounts/{accountId}/cards`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/cards" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Retrieve All Cards

To retrieve a list of all cards associated with the programme, make a `GET` request to `/cards`. To fetch details of a specific card by ID, use a `GET` request to `/cards/{cardId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Retrieve an Individual Card

To retrieve information about an individual card, make a `GET` request to `/cards/{cardId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards/{cardId}" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Deactivate a Card

To deactivate a card, use a `PATCH` request to `/cards/{cardId}/deactivate`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards/{cardId}/deactivate" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Reactivate a Card

To reactivate a previously deactivated card, use a `PATCH` request to `/cards/{cardId}/activate`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards/{cardId}/activate" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Terminate a Card

A card can be terminated using a `DELETE` request to `/cards/{cardId}`. This is an irreversible operation, and the card will not be available for new payments. However, it will remain active for pending holds for up to 30 days.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards/{cardId}" method="delete" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Transactions

## Authorisation

Before a debit (payment) transaction is created, we usually receive an authorisation request from a payment network. We then either approve or reject the request based on various parameters, including customer and account statuses, on-chain balances etc. If authorisation is approved and authorised funds are claimed by the merchant, we proceed with creating a transaction entity.

### Retrieving a Specific Authorisation

For the time being we only support retrieving an individual authorisation entity, however we will be expanding our API soon to support more endpoints.

To retrieve an individual authorisation, make a `GET` request to `/authorisations/{authorisationId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/authorisations/{authorisationId}" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Authorisation Entity Updates

Any internal updates to the authorisation entity will be reported through webhook update events. For details on how to set up and manage webhook subscriptions, please refer to [Webhooks](/developer-guides/api-integration/api-reference/webhooks).

## Transaction

### Retrieving Transactions for a Specific Account or Card

To obtain a comprehensive list of transactions for a specific account or card, initiate a `GET` request to the corresponding endpoint:

* `/accounts/{accountId}/transactions` for account transactions

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/transactions" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

* `/cards/{cardId}/transactions` for card transactions

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards/{cardId}/transactions" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Transaction Entity Updates

Any internal updates to the transaction entity will be reported through webhook update events. For details on how to set up and manage webhook subscriptions, please refer to [Webhooks](/developer-guides/api-integration/api-reference/webhooks).

### Retrieving a Specific Transaction

To retrieve detailed information about a specific transaction, make a `GET` request to `/transactions/{transactionId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/transactions/{transactionId}" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Webhooks

API reference for webhook-related endpoints

## Subscription

### Managing Webhook Subscriptions

You can create a new webhook subscription or update an existing one using a `PUT` request to `/webhooks`. Whether you prefer to receive notifications for all entities through a single callback URL or require more granular configuration, you can achieve this by using the `types` parameter in the request body.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/webhooks" method="put" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Retrieving Webhook Subscriptions

You can retrieve and existing webhook subscription using a `GET` request to `/webhooks/{webhookId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/webhooks/{webhookId}" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

To retrieve all active webhook subscriptions, make a `GET` request to `/webhooks`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/webhooks" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Removing Webhook Subscriptions

If you wish to remove a webhook subscription, you can do so by sending a `DELETE` request to `/webhooks/{webhookId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/webhooks/{webhookId}" method="delete" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

## Schema

{% hint style="warning" %}
This portion of our API is in active development and may be updated frequently. Please, reach out to us if something is not working or not working as expected
{% endhint %}

Every webhook event object has a discriminator `webhookType` field followed by all fields included in a respective entity schema:

<details>

<summary>Customer Updated Event</summary>

```json
{
  "webhookType": "customer.updated",
  "eventId": "db63696c-10b2-42c1-8a07-ac9a15985744",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "externalId": "string",
  "chainId": "account-chain-id",
  "status": "initiating",
  "createdAt": "2024-01-11T18:34:38.367Z",
  "updatedAt": "2024-01-11T18:34:38.367Z"
}
```

</details>

<details>

<summary>Account Updated Event</summary>

```json
{
  "webhookType": "account.updated",
  "eventId": "d403095e-df22-4946-9932-63b04ced15b4",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "chainId": "account-chain-id",
  "status": "initiating",
  "type": "card_account",
  "currencies": [
    "string"
  ],
  "createdAt": "2024-01-11T18:34:38.367Z",
  "updatedAt": "2024-01-11T18:34:38.367Z"
}
```

</details>

<details>

<summary>Card Updated Event</summary>

```json
{
  "webhookType": "card.updated",
  "eventId": "7fe835d9-7ab5-4db0-a5fe-e37a86735dc5",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "accountId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "virtual",
  "name": "string",
  "network": "visa",
  "maskedPan": "string",
  "expirationDate": "mm/yyyy",
  "billingAddress": {
    "firstLine": "string",
    "secondLine": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postCode": "string"
  },
  "status": "issuing",
  "createdAt": "2024-01-11T18:34:38.367Z",
  "updatedAt": "2024-01-11T18:34:38.367Z"
}
```

</details>

<details>

<summary>Authorisation Updated Event</summary>

```json
{
  "webhookType": "authorisation.updated",
  "eventId": "64727de0-1245-4a12-a8a7-bbe8383d9cfd",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "accountId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "cardId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "pending",
  "amount": "decimal",
  "currency": "string",
  "merchantAmount": "decimal",
  "merchantCurrency": "string",
  "exchangeRate": "decimal",
  "merchant": {
    "id": "string",
    "name": "string",
    "mcc": 0,
    "country": "string",
    "city": "string"
  },
  "createdAt": "2024-01-11T18:34:38.368Z",
  "updatedAt": "2024-01-11T18:34:38.368Z"
}
```

</details>

<details>

<summary>Transaction Updated Event</summary>

```json
{
  "webhookType": "transaction.updated",
  "eventId": "64727de0-1245-4a12-a8a7-bbe8383d9cfd",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "accountId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "cardId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "authorisationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "processing",
  "direction": "debit",
  "amount": "decimal",
  "currency": "string",
  "merchantAmount": "decimal",
  "merchantCurrency": "string",
  "exchangeRate": "decimal",
  "merchant": {
    "id": "string",
    "name": "string",
    "mcc": 0,
    "country": "string",
    "city": "string"
  },
  "tokenAmount": "decimal",
  "token": "asset-chain-id",
  "fees": [
    {
      "type": "partner",
      "tokenAmount": "decimal"
    }
  ],
  "chainTransactions": [
    {
      "chainId": "transaction-chain-id",
      "createdAt": "2024-01-11T18:34:38.368Z"
    }
  ],
  "createdAt": "2024-01-11T18:34:38.368Z",
  "updatedAt": "2024-01-11T18:34:38.368Z"
}
```

</details>

## Webhook retry policy

We consider a webhook successfully delivered when we receive a success status code (2xx) from your webhook URI.

If we receive any other status code (for instance, if your API is temporarily unavailable), we will start retrying. Our retry policy is similar to [jittered exponential backoff](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/). We will immediately perform some fast retries and then start waiting increasingly longer. We will keep retrying for up to 24 hours. If we continue to receive any other status codes than 2xx after retrying for 24 hours, we will discard the webhook.

We apply this retry policy for all supported entities.

## Handling duplicated webhooks

OffBlocks can't guarantee that you only receive a single webhook notification for each update. As such, your integration should have logic that can handle receiving multiple webhooks for a given update. Similarly, due to networking limitations, we can't guarantee webhooks to be delivered in a correct chronological order. Your system needs to be able to handle out-of-order notifications.

For example, imagine OffBlocks sends a `transaction.updated` webhook with status `processed`, but doesn't receive a 200 response due to network issues from the recipient. In this case, OffBlocks sends an extra `transaction.updated` webhook with status `processed` as it can't confirm the previous one was received, regardless of the current status of the transaction. Your integration should be able to handle such possibilities.

## Webhook Notification Verification

Every webhook notification we send contains an HTTP signature that you can verify to ensure it has been sent from us, a verified source. Although it is not required, we strongly recommend verifying notification signature, or you risk accepting fraudulent events.

Each webhook notification contains a set of headers for you to verify, following the signing procedure outlined in [Request Signatures](/developer-guides/api-integration/request-signatures). There are a few steps required to verifying the webhook signatures. We'll go through each one below.

### Retrieve Webhook Verification Key

To verify the signature, you need to grab a public key (encoded in [PEM format](https://datatracker.ietf.org/doc/html/rfc7468)). This is available through our REST API and can be retrieved by doing a `GET` call on the `/webhooks/verification-key` endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/webhooks/verification-key" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Extract Webhook Headers

On each webhook HTTP request, there are three headers that will be needed for verification.

* `Content-Digest`: a `sha-512` hash of serialised request content, or body.
* `Signature-Input`: a Dictionary structured field containing the metadata for a signatures generated from components within the HTTP request (see [IETF specification](https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-19.html#name-the-signature-input-http-fi) for more details)
* `Signature`: a Dictionary structured field containing a message signatures generated from the signature context of the target message (see [IETF specification](https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-19.html#name-the-signature-http-field) for more details)

All webhook requests are signed using [ECDSA with curve P-384 DSS and SHA-384](https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html#name-ecdsa-using-curve-p-384-dss) as a signing algorithm, while digest is always calculated using `sha-512` hashing algorithm.

### Verification

Now that you have the webhook body, webhook signature headers, and the public key, you can use these data points to do the verification.

We actively develop a [Go library](https://pkg.go.dev/github.com/offblocks/httpsig) to support HTTP message signatures as well as contribute to a [Node.js library](https://www.npmjs.com/package/http-message-signatures). Reach out to us if you're using other technologies on your backend and we'll be happy to assist with any issues integrating HTTP message signatures.

For example, if you're using Go-based backend, you can use following code snippet to verify webhook request's digest and signature:

```go
reader := req.Body

body, err := io.ReadAll(reader)
if err != nil {
	w.WriteHeader(http.StatusBadRequest)
	return
}

block, _ := pem.Decode([]byte(key))

pki, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
	w.WriteHeader(http.StatusBadRequest)
	return
}

pkpub := pki.(*ecdsa.PublicKey)
verifier := httpsig.NewVerifier(
	httpsig.WithVerifyEcdsaP384Sha384(keyId, pkpub),
)

digestor := httpsig.NewDigestor(httpsig.WithDigestAlgorithms(httpsig.DigestAlgorithmSha512))

err = digestor.Verify(body, req.Header)
if err != nil {
	w.WriteHeader(http.StatusBadRequest)
	return
}

err = verifier.Verify(httpsig.MessageFromRequest(req))
if err != nil {
	w.WriteHeader(http.StatusBadRequest)
	return
}
```


# Simulator

## Customer Verifications

### Verify Customer

To verify a customer with pending verification and proceed with activation, make a `PATCH` request to `/simulator/customers/{customerId}/verify`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/customers/{customerId}/verify" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Reject Customer

To reject a customer with pending verification, make a `PATCH` request to `/simulator/customers/{customerId}/reject`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/customers/{customerId}/reject" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

## Cards

### Issue Card

To approve a pending card issuance request and proceed with activation, make a `PATCH` request to `/simulator/cards/{cardId}/issue`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/cards/{cardId}/issue" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Fail Card Issuance

To fail a pending card issuance request, make a `PATCH` request to `/simulator/cards/{cardId}/fail`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/cards/{cardId}/fail" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

## Authorisations

### Initiate Card Authorisation

To initiate a card authorisation for a specified card, make a `POST` request to `/simulator/cards/{cardId}/authorisation`. You have an option to automatically capture the created authorisation upon approval.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/cards/{cardId}/authorisation" method="post" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Capture Authorisation

To capture a pending card authorisation which was not initiated with automatic capture mode, make a `PATCH` request to `/simulator/authorisations/{authorisationId}/capture`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/authorisations/{authorisationId}/capture" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

### Release Authorisation

To release a pending card authorisation which was not initiated with automatic capture mode, make a `PATCH` request to `/simulator/authorisations/{authorisationId}/release`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/authorisations/{authorisationId}/release" method="patch" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Engine

### Supported Blockchains

To retrieve a list of supported blockchains, make a `GET` requests to `/chains`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/chains" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

This list contains names and [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) compliant chain IDs for all blockchains currently supported by this API instance. Normally, for Sandbox environment this will include all supported testnets and devnets, while for Production this includes mainnets.

For more information on supported blockchains and their status please refer to [Supported Blockchains and Assets](/overview/supported-blockchains-and-assets).

### Supported Assets

To retrieve a list of supported assets, make a `GET` requests to `/assets`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/assets" method="get" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

This list contains names, symbols and [CAIP-19](https://chainagnostic.org/CAIPs/caip-19) compliant asset IDs for all assets currently supported by this API instance. Normally, for Sandbox environment this will include all supported assets on testnets and devnets, while for Production this includes assets on mainnets.

For more information on supported assets and their status please refer to [Supported Blockchains and Assets](/overview/supported-blockchains-and-assets).


# States and Transitions

Every entity in our API has its own state machine and transition rules aiming at helping you to understand the lifecycle of an entity and current state of the integration.

{% content-ref url="/pages/3zksW8JdsRiBuT1LKpnj" %}
[Customer States](/developer-guides/api-integration/states-and-transitions/customer-states)
{% endcontent-ref %}

{% content-ref url="/pages/T4x7ckXawQcl0x266zpU" %}
[Account States](/developer-guides/api-integration/states-and-transitions/account-states)
{% endcontent-ref %}

{% content-ref url="/pages/Ax3KoIxyvMfzmEH6YSxA" %}
[Card States](/developer-guides/api-integration/states-and-transitions/card-states)
{% endcontent-ref %}

{% content-ref url="/pages/KpJmarRks7WQ6YQRfnfu" %}
[Authorisation States](/developer-guides/api-integration/states-and-transitions/authorisation-states)
{% endcontent-ref %}

{% content-ref url="/pages/p6zIhu52G9o9h3QeP3e3" %}
[Transaction States](/developer-guides/api-integration/states-and-transitions/transaction-states)
{% endcontent-ref %}


# Customer States

* `initiating` - an initial state for a Customer. This state is assigned to an entity immediately upon creation via [Customers](/developer-guides/api-integration/api-reference/customers#customers) endpoint. At this stage we expect a customer challenge to be signed and verified before proceeding to next states. Upon successful challenge verification an entity is transitioned to `screening` state
* `screening` - a Customer entity is undergoing blockchain screening through a third-party service. Upon completion of the screening process and if the result is satisfactory for our processes, an entity is transitioned to `verifying` state. If screening result is unsatisfactory, an entity is transitioned to `rejected`
* `verifying` - a Customer entity is undergoing KYC verification through a third-party service. At this stage you can retrieve customer verification details required to initiate a KYC session with our partner. Details on this process are to be added to this guide. Upon completion and successful customer verification, an entity is transitioned to `active`, otherwise - to `rejected`
* `rejected` - a Customer entity has failed one of the previous verification steps and is not allowed to use our services. This is a **final** state. The algorithms used at verification steps are able to determine false-positives and employ retry mechanisms. If a customer was not able to satisfy the requirement, we are confident they are never going to satisfy them and we won't allow them to be onboarded again
* `active` - a Customer entity is ready to use our services, including account and card creation. An entity can be temporarily deactivated in case of any issues or if we suspect any illicit activity. In this case an entity is transitioned to `inactive` state. You can also deactivate Customer yourself using [Customers](/developer-guides/api-integration/api-reference/customers#customers-customerid-deactivate) endpoint. If we have evidence of any illicit activity received through our constant monitoring workflows, an entity is transitioned to `rejected` state
* `inactive` - a Customer entity is temporarily deactivated. It can be re-activated again on our end or using [Customers](/developer-guides/api-integration/api-reference/customers#customers-customerid-activate) endpoint


# Account States

* `initiating` - an initial Account state. This state is immediately assigned to an entity created using [Accounts](/developer-guides/api-integration/api-reference/accounts#customers-customerid-accounts) endpoint provided it has passed basic validations (such as whether the input is correct, requested currencies are supported and customer is active). If validations were failed, account is transitioned to `failed` state. At this stage we expect an account challenge to be signed and verified before proceeding to next states. Upon successful challenge verification an entity is transitioned to `pending` state
* `pending` - an Account is being deployed on-chain. Upon completion it is transitioned to `active` and `chainId` is assigned
* `active` - an Account has been successfully deployed and is ready to be used to issue cards and perform payments. An entity can be temporarily deactivated in case of any issues or if we suspect any illicit activity. In this case an entity is transitioned to `inactive` state. You can also deactivate Account yourself using [Accounts](/developer-guides/api-integration/api-reference/accounts#accounts-accountid-deactivate) endpoint. If we have evidence of any illicit activity received through our constant monitoring workflows, an entity is transitioned to `closed` state
* `inactive` - an Account entity is temporarily deactivated. It can be re-activated again on our end or using [Accounts](/developer-guides/api-integration/api-reference/accounts#accounts-accountid-activate) endpoint
* `closed` - an Account has been closed and is no longer available for our services. This can be done via [Accounts](/developer-guides/api-integration/api-reference/accounts#accounts-accountid-1) endpoint. This is a **final** state
* `failed` - an Account creation request has failed some of the validations. A new request must be submitted. This is a **final** state


# Card States

* `issuing` - an initial state for a Card. This state is immediately assigned to an entity created via [Cards](/developer-guides/api-integration/api-reference/cards#accounts-accountid-cards) endpoint. At this stage we perform request validations (checking whether a Customer and an Account are active) and submit an issuing request to our banking partner. If our validations or partner request failed, an entity is transitioned to `issue_failed` state
* `issue_failed` - a Card issuance request did not pass validations or was rejected by our banking partner. A new request must be submitted. This is a **final** state
* `active` - a Card is successfully issued and can be used to make payments. An entity can be temporarily deactivated in case of any issues. In this case an entity is transitioned to `inactive` state. You can also deactivate Card yourself using [Cards](/developer-guides/api-integration/api-reference/cards#cards-cardid-deactivate) endpoint. If we suspect any illicit activity, a Card is transitioned to `suspended` state. If we have evidence of any illicit activity received through our constant monitoring workflows, an entity is transitioned to `blocked` state. Finally, if card termination was requested via [Cards](/developer-guides/api-integration/api-reference/cards#cards-cardid-1) endpoint, it is transitioned to `terminated`
* `inactive` - a Card is temporarily deactivated. It can be re-activated using [Cards](/developer-guides/api-integration/api-reference/cards#cards-cardid-activate) endpoint
* `blocked` - we have evidence of illicit activity by the cardholder. This is a **final** state
* `suspended` - a Card has been suspended as we suspect illicit activity by the cardholder. If suspicions are not confirmed, a Card is transitioned back to `active`, otherwise - to `blocked`
* `terminated` - a Card termination has been requested. This is a **final** state. *Please, note, that while this card is no longer available for new payments and authorisations, we have to respect all fund capture requests for pending authorisation within 30 days from termination date*


# Authorisation States

* `pending` - an initial state for an Authorisation. This state is immediately assigned to an Authorisation once an authorisation request has been received and approved. In parallel, we submit on-chain transactions to reserve account funds in an escrow. At this stage we are waiting for capture, release and update requests from a payment network. If an Authorisation has been captures by the merchant, an entity is transitioned to `capturing` state. If it has been released - to `releasing`. Authorisation updates will not result in state change
* `capturing` - an Authorisation is being captured. We submit necessary transactions on-chain to finalise escrow reservation and start settlement flow. Upon completion, an entity is transitioned to `captured`
* `captured` - an Authorisation has been captured and all necessary on-chain transactions have been processed. This is a **final** state
* `releasing` - an Authorisation is being released. We submit necessary transactions on-chain to release escrow reservation and refund customer's account. Upon completion, an entity is transitioned to `released`
* `released` - an Authorisation has been released and all necessary on-chain transactions have been processed. This is a **final** state


# Transaction States

* `processing` - an initial state for a Transaction. This state is immediately assigned to a Transaction upon Authorisation capture or refund request from a payment network. At this stage we are running some final internal checks and are waiting for settlement flow to finalise. Upon completion, an entity is transitioned to `processed`
* `processed` - a Transaction has been processed and finalised with all parties. This state **may** be final, however if a reversal has been requested, an entity is transitioned to `reversing`
* `reversing` - a Transaction reversal has been requested from a payment network. At this stage we are reversing fund settlement and refunding customer's account. Upon completion, an entity is transitioned to `reversed`
* `reversed` - a Transaction has been reversed and finalised with all parties. This is a **final** state


# Smart Contracts

OffBlocks API is orchestrating a smart contract protocol designed to facilitate card payments while preserving self-custody invariants. In order to facilitate such payments, we have developed two main components:

* Smart Wallet
* Escrow Smart Contract

The following diagram visualises how OffBlocks API orchestrates the payment and how tokens are transferred throughout the payment lifecycle:

<div data-full-width="false"><figure><img src="https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FnJCnaSxOKAb9wYDjKDsa%2Fmermaid-diagram-2024-02-13-140547.png?alt=media&amp;token=db839e22-3868-485b-a3da-5610c3519071" alt=""><figcaption></figcaption></figure></div>

***OffBlocksEscrow*** contract is deployed to the following blockchains (testnets only for now):

<table><thead><tr><th width="244">Chain</th><th>Address</th></tr></thead><tbody><tr><td>Ethereum Sepolia</td><td><a href="https://sepolia.etherscan.io/address/0x3368F736Ce54d40248E109C65E7a9296B107e574#code">0x3368F736Ce54d40248E109C65E7a9296B107e574</a></td></tr><tr><td>Arbitrum Sepolia</td><td><a href="https://sepolia.arbiscan.io/address/0x3368F736Ce54d40248E109C65E7a9296B107e574#code">0x3368F736Ce54d40248E109C65E7a9296B107e574</a></td></tr><tr><td>Polygon Mumbai</td><td><a href="https://mumbai.polygonscan.com/address/0x3368F736Ce54d40248E109C65E7a9296B107e574#code">0x3368F736Ce54d40248E109C65E7a9296B107e574</a></td></tr><tr><td>zkSync Sepolia</td><td><em>Coming soon</em></td></tr></tbody></table>

{% hint style="info" %}
Contracts for ***OffBlocksSmartWallet*** and ***OffBlocksSmartWalletFactory*** will be added soon
{% endhint %}


# OffBlocksEscrow\.sol

Protocol documentation for the OffBlocksEscrow\.sol smart contract.

### Contract Overview

#### Inherits:

* `ReentrancyGuardUpgradeable`, `OwnableUpgradeable` & `PausableUpgradeable` from OpenZeppelin
* Also imports `IERC20` & `IOffBlocksSmartWalletFactory` interfaces which are used in the contract

**Author:** OffBlocks Team

**Description:** This escrow contract handles token deposits and withdrawals coming from smart wallets, specifically designed for the OffBlocks platform. It supports deposit operations, deposit status updates, withdrawals for rejected deposits, and settlement of approved deposits.

**Main Features:**

* Handling token deposits from smart wallets.
* Approval, rejection, and settlement of deposits.
* Support for multiple tokens, with functionality to add or remove tokens.
* Integration with OffBlocks smart wallet factory for smart wallet verification.
* Emergency withdrawal mechanism for added security.

### State Variables

1. **smartWalletFactory (`IOffBlocksSmartWalletFactory`):** Address of the OffBlocks smart wallet factory contract. It is used to verify whether a depositor's address is a smart wallet.
2. **supportedTokens (`mapping(IERC20 => bool)`):** A mapping that tracks which tokens are supported by the escrow. Only supported tokens can be deposited.
3. **deposits (`mapping(address => uint256[])`):** A mapping from a depositor's address to an array of their deposit IDs. It keeps track of all deposits made by each depositor.
4. **depositById (`mapping(uint256 => Deposit)`):** A mapping from a deposit ID to its corresponding `Deposit` struct. This struct contains details about each deposit.
5. **\_nextDepositId (`uint256`):** An internal variable that keeps track of the next available deposit ID. It ensures that each deposit has a unique identifier.

### Structs

1. **Deposit:**
   * `depositId (uint256)`: Unique identifier for the deposit.
   * `depositor (address)`: Address of the depositor, which must be a smart wallet.
   * `token (address)`: Address of the token being deposited.
   * `amount (uint256)`: Amount of the token deposited.
   * `splitAmounts (uint256[])`: Array of amounts that correspond to how the deposit should be split upon approval.
   * `splitAddresses (address[])`: Addresses to which the split amounts will be sent upon deposit approval.
   * `status (DepositStatus)`: Current status of the deposit (Pending, Approved, or Rejected).
   * `createdAt (uint256)`: Timestamp when the deposit was created.
   * `updatedAt (uint256)`: Timestamp when the deposit was last updated.
2. **DepositStatus (`enum`):** Represents the possible states of a deposit: `Pending`, `Approved`, or `Rejected`.

### Events

1. **Deposited:**
   * Emitted when a new deposit is made into the escrow.
   * Parameters: `depositId`, `depositor`, `token`, `amount`, `createdAt`.
2. **DepositToppedUp:**
   * Emitted when an existing deposit is topped up with an additional amount.
   * Parameters: `depositId`, `depositor`, `token`, `amount`, `updatedAt`.
3. **DepositReduced:**
   * Emitted when the amount of an existing deposit is reduced.
   * Parameters: `depositId`, `depositor`, `token`, `amount`, `updatedAt`.
4. **DepositStatusUpdated:**
   * Emitted when the status of a deposit is updated to either Approved or Rejected.
   * Parameters: `depositId`, `status`, `updatedAt`.
5. **Withdrawal:**
   * Emitted when a rejected deposit is withdrawn back to the depositor.
   * Parameters: `depositId`, `depositor`, `token`, `amount`, `timestamp`.
6. **Settlement:**
   * Emitted when an approved deposit is settled, i.e., the deposited tokens are transferred according to the split configuration.
   * Parameters: `depositId`, `depositor`, `token`, `amount`, `timestamp`.
7. **NewSupportedToken:**
   * Emitted when a new token is added to the list of supported tokens.
   * Parameter: `token`.
8. **RemovedSupportedToken:**
   * Emitted when a token is removed from the list of supported tokens.
   * Parameter: `token`.
9. **SmartWalletFactoryUpdated:**
   * Emitted when the smart wallet factory address is updated.
   * Parameter: `smartWalletFactory`.
10. **RescueTokens:**
    * Emitted when the contract owner rescues ERC20 tokens sent to the contract by mistake.
    * Parameters: `to`, `token`, `amount`.

### Errors

1. **ZeroAddress:**
   * Thrown when a function receives the zero address where it is not allowed.
2. **ZeroAmount:**
   * Thrown when an operation involving tokens or Ether is attempted with a zero amount.
3. **InsufficientBalance:**
   * Thrown when a depositor does not have enough balance of a token to make a deposit.
4. **InsufficientAllowance:**
   * Thrown when the contract does not have enough allowance to transfer tokens on behalf of a depositor.
5. **UnsupportedToken:**
   * Thrown when an attempt is made to deposit a token not supported by the escrow.
   * Parameter: `token`.
6. **InvalidStatus:**
   * Thrown when an invalid status is passed to a function.
   * Parameter: `status`.
7. **DepositNotPending:**
   * Thrown when an operation that requires a deposit to be in the Pending status is attempted on a deposit with a different status.
8. **DepositNotApproved:**
   * Thrown when an operation that requires a deposit to be in the Approved status is attempted on a deposit with a different status.
9. **DepositNotRejected:**
   * Thrown when an operation that requires a deposit to be in the Rejected status is attempted on a deposit with a different status.
10. **InvalidDepositId:**
    * Thrown when an operation is attempted on a deposit with an ID that does not exist.
    * Parameter: `depositId`.
11. **ERC20TransferFailed:**
    * Thrown when a transfer of ERC20 tokens fails.
12. **ArrayLengthMismatch:**
    * Thrown when the lengths of two arrays expected to match do not match.
13. **InvalidSplitAddressIncluded:**
    * Thrown when a split address included in a deposit is invalid (e.g., the zero address).
14. **MsgSenderIsNotASmartWallet:**
    * Thrown when a function that expects the caller to be a smart wallet is called by an address that is not a smart wallet.
15. **NewAmountLessThanOldAmount:**
    * Thrown when an attempt is made to top up a deposit with an amount less than the existing deposit amount.
16. **NewAmountGreaterThanOldAmount:**
    * Thrown when an attempt is made to reduce a deposit with an amount greater than the existing deposit amount.
17. **DepositUnchanged:**
    * Thrown when an attempt is made to modify a deposit amount, but the new amount is the same as the old amount.
18. **ZeroTokens:**
    * Thrown when an operation involving the rescue of tokens is attempted, but there are no tokens to rescue.
19. **ArrayLengthOfZero:**
    * Thrown when an operation involving an array is attempted with an array of length zero.

### Modifiers

There are no custom modifiers defined in the contract beyond what's inherited from OpenZeppelin's `ReentrancyGuard`, `Ownable`, and `Pausable` contracts, which include standard modifiers like `nonReentrant`, `onlyOwner`, and `whenNotPaused`/`whenPaused`.

### Initializer

**initialize**

```solidity
function initialize(address _smartWalletFactory, address[] memory _initiallySupportedTokens, address _owner) external initializer
```

* **Description**: Since the OffBlocksEscrow contract is intended to be upgradeable through the use of TransparentUpgradeableProxy pattern, it needs an initializer function. It initializes the escrow contract with a smart wallet factory, a list of initially supported tokens, and sets the contract owner.
* **Inputs**:
  * `_smartWalletFactory`: Address of the OffBlocks smart wallet factory contract.
  * `_initiallySupportedTokens`: Array of token addresses initially supported by the escrow.
  * `_owner`: Address of the contract owner.
* **Notes**: This function replaces the constructor for upgradeable contracts and can only be called once.

### Functions

**addSupportedToken**

```solidity
function addSupportedToken(address _token) external onlyOwner
```

* **Description**: Adds a token to the list of tokens supported by the escrow for deposit.
* **Inputs**:
  * `_token`: The address of the token to be added to the list of supported tokens.
* **Modifiers**: `onlyOwner` - Ensures that only the contract owner can add supported tokens.

**removeSupportedToken**

```solidity
function removeSupportedToken(address _token) external onlyOwner
```

* **Description**: Removes a token from the list of tokens supported by the escrow.
* **Inputs**:
  * `_token`: The address of the token to be removed from the list of supported tokens.
* **Modifiers**: `onlyOwner` - Restricts this function to the contract owner.

**updateSmartWalletFactory**

```solidity
function updateSmartWalletFactory(address _smartWalletFactory) external onlyOwner
```

* **Description**: Updates the address of the OffBlocks smart wallet factory.
* **Inputs**:
  * `_smartWalletFactory`: New address of the OffBlocks smart wallet factory.
* **Modifiers**: `onlyOwner` - Only the contract owner can update the smart wallet factory address.

**deposit**

```solidity
function deposit(address _token, uint256[] memory _splitAmounts, address[] memory _splitAddresses) external nonReentrant whenNotPaused
```

* **Description**: Allows a smart wallet to deposit tokens into the escrow, specifying how the tokens should be split upon approval.
* **Inputs**:
  * `_token`: Address of the token being deposited.
  * `_splitAmounts`: Array of amounts for each split.
  * `_splitAddresses`: Corresponding addresses for each split amount.
* **Modifiers**:
  * `nonReentrant`: Prevents re-entrancy attacks.
  * `whenNotPaused`: Function can only be called when the contract is not paused.

**topUpDeposit**

```solidity
function topUpDeposit(uint256 _depositId, uint256[] memory _splitAmounts) external nonReentrant whenNotPaused
```

* **Description**: Tops up an existing deposit with additional tokens.
* **Inputs**:
  * `_depositId`: ID of the deposit to be topped up.
  * `_splitAmounts`: New split amounts, updating the deposit's split configuration.
* **Modifiers**:
  * `nonReentrant`
  * `whenNotPaused`

**reduceDeposit**

```solidity
function reduceDeposit(uint256 _depositId, uint256[] memory _splitAmounts) external nonReentrant onlyOwner
```

* **Description**: Reduces the amount of an existing deposit and adjusts the split configuration.
* **Inputs**:
  * `_depositId`: ID of the deposit to be reduced.
  * `_splitAmounts`: Adjusted split amounts.
* **Modifiers**:
  * `nonReentrant`
  * `onlyOwner`

**batchUpdateDepositStatuses**

```solidity
function batchUpdateDepositStatuses(uint256[] calldata _depositIds, uint256 _status) external onlyOwner
```

* **Description**: Batch updates the statuses of multiple deposits.
* **Inputs**:
  * `_depositIds`: Array of deposit IDs to update.
  * `_status`: New status for the deposits (Pending, Approved, Rejected).
* **Modifiers**: `onlyOwner`

**reject**

```solidity
function reject(uint256 _depositId) external nonReentrant onlyOwner
```

* **Description**: Rejects a deposit, enabling the depositor to withdraw the tokens.
* **Inputs**:
  * `_depositId`: ID of the deposit to be rejected.
* **Modifiers**:
  * `nonReentrant`
  * `onlyOwner`

**settle**

```solidity
function settle(uint256 _depositId) external nonReentrant onlyOwner
```

* **Description**: Transfers tokens from an approved deposit according to its split configuration.
* **Inputs**:
  * `_depositId`: ID of the deposit to be settled.
* **Modifiers**:
  * `nonReentrant`
  * `onlyOwner`

**batchSettle**

```solidity
function batchSettle(uint256[] calldata _depositIds) external onlyOwner
```

* **Description**: Batch settles approved deposits, distributing tokens per their split configurations.
* **Inputs**:
  * `_depositIds`: Array of deposit IDs to be settled.
* **Modifiers**: `onlyOwner`

**pause**

```solidity
function pause() external onlyOwner
```

* **Description**: Pauses the contract, disabling deposits and status updates.
* **Modifiers**: `onlyOwner`

**unpause**

```solidity
function unpause() external onlyOwner
```

* **Description**: Unpauses the contract, enabling deposits and status updates.
* **Modifiers**: `onlyOwner`

**rescueTokens**

```solidity
function rescueTokens(address _token) external onlyOwner
```

* **Description**: Allows the contract owner to rescue mistakenly sent ERC20 tokens.
* **Inputs**:
  * `_token`: Address of the token to be rescued.
* **Modifiers**: `onlyOwner`
* **Emits**: `RescueTokens` event on success.

**getSmartWalletFactory**

```solidity
function getSmartWalletFactory() external view returns (address)
```

* **Description**: Returns the address of the smart wallet factory.
* **Outputs**:
  * `address`: The address of the smart wallet factory used by the escrow.

**getDepositsOfUser**

```solidity
function getDepositsOfUser(address _depositor) external view returns (uint256[] memory)
```

* **Description**: Retrieves the array of deposit IDs associated with a depositor.
* **Inputs**:
  * `_depositor`: Address of the user whose deposit IDs are being queried.
* **Outputs**:
  * `uint256[]`: Array of deposit IDs belonging to the depositor.

**getDepositById**

```solidity
function getDepositById(uint256 _depositId) public view returns (Deposit memory)
```

* **Description**: Fetches the details of a deposit by its ID.
* **Inputs**:
  * `_depositId`: ID of the deposit to fetch.
* **Outputs**:
  * `Deposit`: A struct containing the details of the deposit.

**getNextDepositId**

```solidity
function getNextDepositId() public view returns (uint256)
```

* **Description**: Retrieves the ID that will be assigned to the next deposit.
* **Outputs**:
  * `uint256`: The ID to be assigned to the next deposit.

**\_checkForInvalidAddress**

```solidity
function _checkForInvalidAddress(address[] memory _splitAddresses) internal pure returns (bool)
```

* **Description**: Checks if the provided array of split addresses contains any invalid (zero) addresses.
* **Inputs**:
  * `_splitAddresses`: Array of addresses to check.
* **Outputs**:
  * `bool`: `true` if all addresses are valid; otherwise, `false`.

**\_getSum**

```solidity
function _getSum(uint256[] memory _values) internal pure returns (uint256)
```

* **Description**: Calculates the sum of an array of uint256 values.
* **Inputs**:
  * `_values`: Array of uint256 values to sum.
* **Outputs**:
  * `uint256`: Sum of the values in the array.


# OffBlocksSmartWalletFactory.sol

Protocol documentation for the OffBlocksSmartWalletFactory.sol smart contract.

### Contract Overview

#### Inherits:

* `OwnableUpgradeable` from OpenZeppelin
* Also imports `IERC20` interface from OpenZeppelin which is used in the contract, as well as `OffBlocksSmartWallet` contract which is a contract factory deploys in the `deploySmartWallet` method

**Author:** OffBlocks Team

**Description:** This contract serves as a factory for creating and managing OffBlocks smart wallets, which are deployed on EVM chains supporting ERC-4337. It features functionality for creating new smart wallets, handling withdrawals with a delay mechanism, and maintaining a registry of smart wallets to verify their authenticity.

## Initializer

```solidity
function initialize(address _owner, uint256 _withdrawalDelay) external initializer
```

* **Description**: Sets up the smart wallet factory with an owner and a predefined withdrawal delay.
* **Inputs**:
  * `_owner`: Address of the factory contract owner.
  * `_withdrawalDelay`: Time delay (in seconds) required between withdrawal operations from smart wallets.
* **Notes**: This function initializes the contract and can only be called once during the contract's lifetime.

### Functions

**deploySmartWallet**

```solidity
function deploySmartWallet(address _walletOwner, address _escrow, address _smartWalletFactory, uint256 _salt) external onlyOwner
```

* **Description**: Deploys a new OffBlocks smart wallet with a unique owner, linked to an escrow contract.
* **Inputs**:
  * `_walletOwner`: Address that will own the smart wallet.
  * `_escrow`: Escrow contract address associated with the smart wallet.
  * `_smartWalletFactory`: This factory contract address.
  * `_salt`: A nonce to ensure the uniqueness of the smart wallet deployment.
* **Modifiers**: `onlyOwner`
* **Emits**: `SmartWalletCreated` event upon successful creation.

**updateWithdrawalDelay**

```solidity
function updateWithdrawalDelay(uint256 _newDelay) external onlyOwner
```

* **Description**: Updates the withdrawal delay for all smart wallets.
* **Inputs**:
  * `_newDelay`: New withdrawal delay in seconds.
* **Modifiers**: `onlyOwner`
* **Emits**: `WithdrawalDelayChanged` event indicating the new delay.

**batchRegisterSmartWallets**

```solidity
function batchRegisterSmartWallets(address[] calldata _walletOwners, address[] calldata _smartWallets) external onlyOwner
```

* **Description**: Registers multiple third-party smart wallets to their respective owners.
* **Inputs**:
  * `_walletOwners`: An array of addresses owning the smart wallets.
  * `_smartWallets`: An array of third-party smart wallet addresses to be registered.
* **Modifiers**: `onlyOwner`
* **Emits**: `ThirdPartySmartWalletRegistered` event for each smart wallet registered.

**batchUnregisterSmartWallets**

```solidity
function batchUnregisterSmartWallets(address[] calldata _walletOwners, address[] calldata _smartWallets) external onlyOwner
```

* **Description**: Unregisters multiple third-party smart wallets from their respective owners.
* **Inputs**:
  * `_walletOwners`: An array of addresses owning the smart wallets.
  * `_smartWallets`: An array of third-party smart wallet addresses to be unregistered.
* **Modifiers**: `onlyOwner`
* **Emits**: `ThirdPartySmartWalletUnregistered` event for each smart wallet unregistered.

**rescueTokens**

```solidity
function rescueTokens(address _token) external onlyOwner
```

* **Description**: Allows the owner to rescue ERC20 tokens mistakenly sent to the contract.
* **Inputs**:
  * `_token`: The ERC20 token address to be rescued.
* **Modifiers**: `onlyOwner`
* **Emits**: `RescueTokens` event indicating the successful rescue operation.

**getWithdrawalDelay**

```solidity
function getWithdrawalDelay() external view returns (uint256)
```

* **Description**: Retrieves the current withdrawal delay setting.
* **Outputs**:
  * `uint256`: The withdrawal delay in seconds.

**getIsSmartWallet**

```solidity
function getIsSmartWallet(address _wallet) external view returns (bool)
```

* **Description**: Checks if a given address is registered as an OffBlocks smart wallet.
* **Inputs**:
  * `_wallet`: The address to check.
* **Outputs**:
  * `bool`: True if the address is a registered smart wallet, false otherwise.

**getSmartWalletsOfUser**

```solidity
function getSmartWalletsOfUser(address _walletOwner) external view returns (address[] memory)
```

* **Description**: Lists all smart wallets associated with a given owner.
* **Inputs**:
  * `_walletOwner`: Owner whose smart wallets are to be listed.
* **Outputs**:
  * `address[]`: An array of smart wallet addresses owned by the specified user.

**precomputeSmartWalletAddress**

```solidity
function precomputeSmartWalletAddress(address _walletOwner, address _escrow, address _smartWalletFactory, uint256 _salt) external view returns (address)
```

* **Description**: Precomputes the address of a smart wallet before its deployment using the provided parameters.
* **Inputs**:
  * `_walletOwner`: The intended owner of the smart wallet.
  * `_escrow`: The escrow contract address associated with the smart wallet.
  * `_smartWalletFactory`: The smart wallet factory contract address (typically this contract).
  * `_salt`: A unique nonce used to generate the smart wallet's address.
* **Outputs**:
  * `address`: The precomputed address of the smart wallet.

**registerSmartWallet**

```solidity
function registerSmartWallet(address _walletOwner, address _smartWallet) public onlyOwner
```

* **Description**: Registers a third-party smart wallet under a specific owner.
* **Inputs**:
  * `_walletOwner`: The owner of the smart wallet.
  * `_smartWallet`: The address of the third-party smart wallet to be registered.
* **Modifiers**: `onlyOwner`
* **Emits**: `ThirdPartySmartWalletRegistered` event upon successful registration.

**unregisterSmartWallet**

```solidity
function unregisterSmartWallet(address _walletOwner, address _smartWallet) public onlyOwner
```

* **Description**: Unregisters a previously registered third-party smart wallet.
* **Inputs**:
  * `_walletOwner`: The owner of the smart wallet.
  * `_smartWallet`: The address of the third-party smart wallet to be unregistered.
* **Modifiers**: `onlyOwner`
* **Emits**: `ThirdPartySmartWalletUnregistered` event upon successful unregistration.

#### Modifiers

* **onlyOwner**: Ensures that only the contract owner can invoke the function.

#### Events

Several events are defined to notify external observers of significant actions within the contract:

* `SmartWalletCreated`
* `ThirdPartySmartWalletRegistered`
* `ThirdPartySmartWalletUnregistered`
* `RescueTokens`
* `RescueETH`
* `WithdrawalDelayChanged`

#### Errors

* `DelayOutOfRange`: Triggered when the provided withdrawal delay is outside the allowed range.
* `ZeroAddress`: Indicates that an operation was attempted with the zero address, which is often invalid.
* `ZeroTokens`: Occurs when a token operation involves zero tokens, which is generally not allowed.
* `NotASmartWallet`: Asserts that an address is not recognized as a smart wallet by the factory.
* `ERC20TransferFailed`: Signals a failure in transferring ERC20 tokens.
* `ArrayLengthMismatch`: Indicates that provided arrays do not match in length, which is usually required for batch operations.

This comprehensive overview covers the essential aspects of the `OffBlocksSmartWalletFactory` contract, including its purpose, key functions, and the operational context within the Ethereum or compatible EVM blockchain ecosystem.


# OffBlocksSmartWallet.sol

Protocol documentation for the OffBlocksSmartWallet.sol smart contract.

### Contract Overview

#### Inherits:

* `ReentrancyGuard` & `Ownable` from OpenZeppelin
* Also imports `IOffBlocksEscrow`, `IOffBlocksSmartWallet` & `IOffBlocksSmartWalletFactory` interfaces and  `IERC20` &`ECDSA` from OpenZeppelin which is used in the contract
* PendingWithdrawal

**Author:** OffBlocks Team

**Description**: The `OffBlocksSmartWallet` is designed for EVM chains supporting ERC-4337. It interacts with an escrow contract to facilitate token approvals and deposits, supports nonce-based operations for security, and manages withdrawals through an internal `PendingWithdrawal` contract module.

### Constructor

```solidity
constructor(address _owner, address _escrow, address _smartWalletFactory);
```

* **Inputs**:
  * `_owner`: The owner of the smart wallet.
  * `_escrow`: Address of the OffBlocks escrow contract.
  * `_smartWalletFactory`: Address of the OffBlocks smart wallet factory.
* **Outputs**: None.
* **Description**: Initializes a new `OffBlocksSmartWallet` with the specified owner, linked to a specific escrow and factory contract.

### Functions

**approveTokensToEscrow**

```solidity
function approveTokensToEscrow(address _token, uint256 _amount, uint256 _nonce, bytes calldata _signature) external onlyEscrowOwner;
```

* **Description**: Approves a specified amount of tokens to the escrow contract, using a signature for authorization.
* **Inputs**:
  * `_token`: The token to approve.
  * `_amount`: Amount of tokens to approve.
  * `_nonce`: A nonce for operation uniqueness.
  * `_signature`: An off-chain generated signature for operation verification.
* **Emits**: `ApproveTokensToEscrow` event upon success.

**depositIntoEscrow**

```solidity
function depositIntoEscrow(address _token, uint256[] calldata _splitAmounts, address[] calldata _splitAddresses) external nonReentrant onlyEscrowOwner;
```

* **Description**: Deposits tokens into the escrow contract for split distribution.
* **Inputs**:
  * `_token`: The token to deposit.
  * `_splitAmounts`: Array of amounts for split distribution.
  * `_splitAddresses`: Array of addresses for receiving the split amounts.
* **Emits**: `DepositIntoEscrow` event upon success.

**topUpDepositIntoEscrow**

```solidity
function topUpDepositIntoEscrow(uint256 _depositId, uint256[] calldata _splitAmounts) external nonReentrant onlyEscrowOwner;
```

* **Description**: Tops up an existing deposit in the escrow contract.
* **Inputs**:
  * `_depositId`: The ID of the deposit to top up.
  * `_splitAmounts`: New split amounts for the deposit.
* **Emits**: `TopUpDepositIntoEscrow` event upon success.

**initiateTokenWithdrawal**

```solidity
function initiateTokenWithdrawal(address _token, uint256 _amount) external nonReentrant onlyOwner;
```

* **Description**: Initiates a withdrawal of tokens to the owner after a delay.
* **Inputs**:
  * `_token`: The token to withdraw.
  * `_amount`: The amount of tokens to withdraw.

**claimTokenWithdrawal**

```solidity
function claimTokenWithdrawal(address _token) external nonReentrant onlyOwner;
```

* **Description**: Claims a pending token withdrawal after the delay.
* **Inputs**:
  * `_token`: The token to claim withdrawal for.

**getSmartWalletOwner**

```solidity
function smartWalletOwner() public view returns (address);
```

* **Description**: Returns the address of the owner of the smart wallet.
* **Outputs**: Address of the smart wallet owner.

**escrowOwner**

```solidity
function escrowOwner() public view returns (address);
```

* **Description**: Returns the address of the owner of the linked escrow contract.
* **Outputs**: Address of the escrow contract owner.

**\_validateNonce**

```solidity
function _validateNonce(uint256 _nonce) internal;
```

* **Description**: Validates the provided nonce to ensure it matches the expected sequence, incrementing the wallet's nonce upon success.
* **Inputs**: `_nonce` - The nonce to validate.

**\_validateSignature**

```solidity
function _validateSignature(bytes32 _hash, bytes calldata _signature) internal view;
```

* **Description**: Validates the provided signature against the expected hash and the wallet owner's address.
* **Inputs**:
  * `_hash` - The hash of the signed data.
  * `_signature` - The signature to validate.

**\_getHash**

```solidity
function _getHash(bytes memory _data) internal pure returns (bytes32);
```

* **Description**: Computes the hash of the given data.
* **Inputs**: `_data` - The data to hash.
* **Outputs**: The hash of the data.

**\_getSum**

```solidity
function _getSum(uint256[] memory _values) internal pure returns (uint256);
```

* **Description**: Calculates the sum of the given array of values.
* **Inputs**: `_values` - An array of values to sum.
* **Outputs**: The sum of the values.

#### Modifiers

* **onlyEscrowOwner**: Restricts function calls to the owner of the escrow contract.

#### Events

* `ApproveTokensToEscrow`: Emitted when tokens are approved to the escrow.
* `DepositIntoEscrow`: Emitted upon a token deposit into the escrow.
* `TopUpDepositIntoEscrow`: Emitted when a deposit in the escrow is topped up.

#### Errors

* `ZeroAddress`: Thrown when a zero address is provided where it is not allowed.
* `ZeroValue`: Thrown when an ether transfer with a zero amount is attempted.
* `ZeroTokens`: Thrown when a token operation involves a zero amount.
* `InsufficientBalance`: Indicates the wallet has insufficient token balance for an operation.
* `InvalidNonce`: Thrown when an invalid nonce is used, preventing replay attacks.
* `InvalidSignature`: Thrown when a provided signature does not match the expected signer.
* `TokenApprovalFailed`: Indicates a failure in token approval to the escrow.
* `WithdrawalDelayNotPassed`: Thrown when a withdrawal is attempted before the delay period ends.
* `ERC20TransferFailed`: Indicates a failure in transferring ERC20 tokens.
* `EtherTransferFailed`: Indicates a failure in transferring ether.
* `OnlyEscrowOwner`: Restricts certain operations to the escrow contract owner.


# PendingWithdrawal.sol

Protocol documentation for the PendingWithdrawal.sol smart contract.

### Contract Overview

#### Inherits:

* ReentrancyGuard & Ownable from OpenZeppelin
* Also imports `IERC20` interface from OpenZeppelin which is used in the contract

**Author:** OffBlocks Team

**Description:** This contract manages pending withdrawals, allowing a delay period before tokens can be claimed, adding an additional layer of security to the withdrawal process.

### State Variables

* **timestamps** (`mapping(address => uint256)`): Maps token addresses to their withdrawal timestamp. It stores the time when a withdrawal can be claimed for each token.

### Errors

* **ZeroAddress**: Triggered when an operation involves the zero address, which is usually invalid.
* **ZeroTokens**: Occurs when an operation involves zero tokens, indicating either an invalid operation or that there are no tokens to operate with.
* **WithdrawalDelayNotPassed**: Emitted when an attempt to claim a withdrawal is made before the specified delay has passed.
* **CancelTokenWithdrawalFailed**: Indicates a failure in the token withdrawal cancellation process.
* **ClaimTokenWithdrawalFailed**: Indicates a failure in the token withdrawal claiming process.

### Events

* **InitiateWithdrawal**: Emitted when a token withdrawal is initiated.
  * Parameters: `address indexed token`, `uint256 delay`
* **ClaimWithdrawal**: Emitted when a token withdrawal is successfully claimed.
  * Parameters: `address indexed to`, `address indexed token`, `uint256 amount`
* **CancelWithdrawal**: Emitted when a token withdrawal is canceled.
  * Parameters: `address indexed to`, `address indexed token`, `uint256 amount`

### Constructor

```solidity
constructor(address _owner) Ownable(_owner);
```

* **Description**: Initializes the `PendingWithdrawal` contract with the smart wallet owner's address.
* **Inputs**:
  * `_owner`: The address of the contract owner, typically a user's smart wallet.

### Functions

**initiateTokenWithdrawal**

```solidity
function initiateTokenWithdrawal(address _token, uint256 _delay) external nonReentrant onlyOwner;
```

* **Description**: Initiates a withdrawal for a specified token with a delay.
* **Inputs**:
  * `_token`: The address of the token.
  * `_delay`: The delay before the withdrawal can be claimed.
* **Modifiers**: `nonReentrant`, `onlyOwner`

**cancelTokenWithdrawal**

```solidity
function cancelTokenWithdrawal(address _token) external nonReentrant onlyOwner;
```

* **Description**: Cancels a pending withdrawal for a specified token.
* **Inputs**:
  * `_token`: The address of the token.
* **Modifiers**: `nonReentrant`, `onlyOwner`

**claimTokenWithdrawal**

```solidity
function claimTokenWithdrawal(address _token, address _dest) external nonReentrant onlyOwner;
```

* **Description**: Claims a pending withdrawal for a specified token after the delay has passed.
* **Inputs**:
  * `_token`: The address of the token.
  * `_dest`: The destination address for the tokens.
* **Modifiers**: `nonReentrant`, `onlyOwner`


# Interfaces


# Step-by-step Guide

The easiest way to get started with OffBlocks API is to follow a step by step guide. It is structured in an incremental way demonstrating building blocks one by one, all the way to the fully working integration.

{% content-ref url="/pages/qMCTZIviWsXIo3Y80FEr" %}
[API Credentials](/developer-guides/step-by-step-guide/api-credentials)
{% endcontent-ref %}

{% content-ref url="/pages/mSHJn3GOMyYMgj2xVKNr" %}
[Authentication](/developer-guides/step-by-step-guide/authentication)
{% endcontent-ref %}

{% content-ref url="/pages/j9kJYOqo03NaDztd1iTa" %}
[Request Preparation](/developer-guides/step-by-step-guide/request-preparation)
{% endcontent-ref %}

{% content-ref url="/pages/qx9XnRJHEVh1Pjpap7A3" %}
[Customer Creation](/developer-guides/step-by-step-guide/customer-creation)
{% endcontent-ref %}

{% content-ref url="/pages/CnsxPcDzY9S4jd8MUWRX" %}
[Customer Verification](/developer-guides/step-by-step-guide/customer-verification)
{% endcontent-ref %}

{% content-ref url="/pages/fzESpnxoLWgGoJ8uygnM" %}
[Account Creation](/developer-guides/step-by-step-guide/account-creation)
{% endcontent-ref %}

{% content-ref url="/pages/JWuEJgooHmltsoM23uFV" %}
[Card Issuance](/developer-guides/step-by-step-guide/card-issuance)
{% endcontent-ref %}

{% content-ref url="/pages/D0LadXtUiryoiJYqfcR0" %}
[Card Authorisations](/developer-guides/step-by-step-guide/card-authorisations)
{% endcontent-ref %}

{% content-ref url="/pages/VJkP95Qd6bDEwzWoePdD" %}
[Webhooks](/developer-guides/step-by-step-guide/webhooks)
{% endcontent-ref %}


# API Credentials

Please contact us at <gm@offblocks.xyz> in order to request your unique API credentials. We will provide you with a pair of `apiKey` and `apiSecret` which you can use in order to authenticate your service with our API.


# Authentication

{% hint style="info" %}
This step assumes you have successfully obtained your API credentials following [API Credentials](/developer-guides/step-by-step-guide/api-credentials) and are ready to go.
{% endhint %}

{% hint style="info" %}
This step is not strictly required if you are using our Postman collection as authentication is done automatically on every request provided that correct API credentials are set in your Postman environment.
{% endhint %}

As stated in [Authentication](/developer-guides/api-integration/authentication) section of our API integration guide, in order to access any API endpoint you need to obtain a JWT token using your previously issued credentials.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/token" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

If your request was successful, you will receive a `token` as part of response, together with its expiration timestamp (by default all tokens expire after 1 hour).

You can now use a valid `token` to make requests to other endpoints of the API by providing it as an `Authorization` header in a following way:

{% code overflow="wrap" %}

```
Authorization: "Bearer <token>"

// For example 
// Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDE1MjY4NTUsIklkIjoiZGZjMTU3MzAtZGI2Zi00MjY1LWE3NjgtNjQ2ODhlNzE4NjA3IiwiR2VuZXJhdGlvbiI6MH0.326XVPWFcc_Z6zg0rCZ5NLou1Mxmql_Ri0r0vD8R4ko"
```

{% endcode %}

If you want to change your API credentials, you can make an authenticated request to the following endpoint. Please note, all previously issued tokens will become invalid.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/auth/credentials" method="put" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Request Preparation

{% hint style="info" %}
This step assumes you have successfully authenticated your API credentials using [Authentication](/developer-guides/step-by-step-guide/authentication)
{% endhint %}

{% hint style="info" %}
This step is not strictly required if you are using our Postman collection as request signatures, content digests and idempotency keys are generated automatically on every request provided that correct variables are set in your Postman environment.
{% endhint %}

Now that you can make authenticated requests to the API, you need to prepare your requests to be processed successfully by our API.

First, you need to sign your requests with HTTP message signatures and include content digest header for requests with request body. To do so, please follow [Request Signatures](/developer-guides/api-integration/request-signatures) guide where you can find details on how to generate signing key pairs and set up our API so that your public key is recognised by us.

Second, for all modification requests (`POST`, `PUT`, `PATCH` and `DELETE`) you need to provide a unique idempotency key to avoid duplicated processing of requests due to networking issues. To do so, please follow [Idempotency](/developer-guides/api-integration/idempotency)guide.


# Customer Creation

{% hint style="info" %}
This step assumes you have successfully authenticated your API credentials using [Authentication](/developer-guides/step-by-step-guide/authentication) and have followed [Request Signatures](/developer-guides/api-integration/request-signatures) and [Idempotency](/developer-guides/api-integration/idempotency) guides to configure your requests
{% endhint %}

Now that you can make authenticated requests to the API, you are officially ready to onboard new customers with our platform. To do so, you need to use `/customers` `POST` request.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Specified `chainId` is expected to follow the convention outlined in [Blockchain Identifiers](/developer-guides/api-integration/blockchain-identifiers#account-identifier). For example, in order to onboard a customer using zkSync Era wallet with address `0x22227A31dd842196A246d8f3b775998560eAa61d` on Goerli Testnet, you can submit a following request:

```json
{
  "chainId": "eip155:280:0x22227A31dd842196A246d8f3b775998560eAa61d",
  "externalId": "27c9aa3b-f12d-43b9-8e91-5264ccc998de"
}
```

If a customer with specified `chainId` and `externalId` is not onboarded in our system yet, you will receive a response with status `201 (Created)` which looks similar to the following:

{% code overflow="wrap" %}

```json
{
    "chainId": "eip155:280:0x22227A31dd842196A246d8f3b775998560eAa61d",
    "challenge": "YXBpLnNhbmRib3gub2ZmYmxvY2tzLnh5eiB3YW50cyB5b3UgdG8gc2lnbiBpbiB3aXRoIHlvdXIgRXRoZXJldW0gYWNjb3VudDoKMHg0MzE1OGY0NWI1RWJEN2IxMTc5MTMwMTMwREYwMDM5MzkyOEMyNjkxCgpPZmZCbG9ja3Mgd2FudHMgdG8gdmVyaWZ5IG93bmVyc2hpcCBvZiB5b3VyIHdhbGxldC4gQnkgY29ubmVjdGluZyB5b3VyIHdhbGxldCBhbmQgdXNpbmcgT2ZmQmxvY2tzLCB5b3UgYWdyZWUgdG8gb3VyIFRlcm1zIG9mIHNlcnZpY2VzIGFuZCBQcml2YWN5IFBvbGljeQoKVVJJOiBodHRwczovL2FwaS5zYW5kYm94Lm9mZmJsb2Nrcy54eXoKVmVyc2lvbjogMQpDaGFpbiBJRDogMjgwCk5vbmNlOiA1dWZvaUVxRGFGdm1mbzc4Cklzc3VlZCBBdDogMjAyNC0wMS0xN1QxODoyMTo1M1o=",
    "createdAt": "2024-01-12T14:53:02.65345725Z",
    "externalId": "27c9aa3b-f12d-43b9-8e91-5264ccc998de",
    "id": "04c5851c-4386-5bd9-9f89-08312f8aee3a",
    "status": "initiating",
    "updatedAt": "2024-01-12T14:53:02.65345725Z"
}
```

{% endcode %}

If everything went well, you will receive a customer challenge to sign as a base64 encoded string. This is required to proceed to verification stage. Please, follow [Sign in With X](/developer-guides/api-integration/sign-in-with-x) guide for more details on signing a customer challenge.

In order to submit a signed challenge, use a `POST` request to `/customers/{customerId}/challenge/sign` endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/challenge/sign" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

{% code overflow="wrap" %}

```json
{
  "ownerId": "eip155:280:0x43158f45b5EbD7b1179130131DF00393928C2691",
  "challenge": "YXBpLnNhbmRib3gub2ZmYmxvY2tzLnh5eiB3YW50cyB5b3UgdG8gc2lnbiBpbiB3aXRoIHlvdXIgRXRoZXJldW0gYWNjb3VudDoKMHg0MzE1OGY0NWI1RWJEN2IxMTc5MTMwMTMwREYwMDM5MzkyOEMyNjkxCgpPZmZCbG9ja3Mgd2FudHMgdG8gdmVyaWZ5IG93bmVyc2hpcCBvZiB5b3VyIHdhbGxldC4gQnkgY29ubmVjdGluZyB5b3VyIHdhbGxldCBhbmQgdXNpbmcgT2ZmQmxvY2tzLCB5b3UgYWdyZWUgdG8gb3VyIFRlcm1zIG9mIHNlcnZpY2VzIGFuZCBQcml2YWN5IFBvbGljeQoKVVJJOiBodHRwczovL2FwaS5zYW5kYm94Lm9mZmJsb2Nrcy54eXoKVmVyc2lvbjogMQpDaGFpbiBJRDogMjgwCk5vbmNlOiA1dWZvaUVxRGFGdm1mbzc4Cklzc3VlZCBBdDogMjAyNC0wMS0xN1QxODoyMTo1M1o=",
  "signature": "MHg4M2IxM2RhYjE2MjI3MjAxMWQzMzQ4NDFkZjE5NTQ1MjUwNDI1NjczMjhhZDdjMDVjOTYyYjAwNWVlZDg3MGI2NDhjMjA0ZjBiZmZjZjUyYjM3NTE0MGIwYzg0OTA4ZTQ2MzMyNWIxZGZlNWJiYjAzMjU5NzYyNDgyODM0YWYzNzFi"
}
```

{% endcode %}

Please, not that the signature also must be a base64 encoded string. If signature is verified, you will receive an empty response with status code `204 (No Content)`.

If, for any reason, the original challenge is lost or cannot be signed immediately, you can fetch it again using a dedicated endpoint. If we encounter an error during signature validation via `/sign` endpoint, a new challenge will be issued for you to retrieve.

We enforce a 1 hour expiration window via our API for all issued challenges for security purposes. If a challenge hasn't been signed within 1 hour from issue, we will generate a new one and make it available through this endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/challenge" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}


# Customer Verification

{% hint style="info" %}
This step assumes you have successfully created a new customer record using [Customer Creation](/developer-guides/step-by-step-guide/customer-creation)step and signed a provided challenge
{% endhint %}

Once the customer challenge has been signed and signature verified, we will proceed to customer screening and verification. Our Sandbox is currently set up to automatically approve the screening stage proceeding to verification. At this stage, you can use our [Simulator](/developer-guides/api-integration/simulator) to verify or reject a newly created customer.

Assuming you would like to verify the customer and proceed with next steps, you need to make an empty `PATCH` request to `/simulator/customers/{customerId}/verify`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/customers/{customerId}/verify" method="patch" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

If your request was successful, you will receive an empty response with status `202 (Accepted)`.

The customer will be automatically activated, which you can verify by making a `GET` request to `/customers/{customerId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

```json
{
    "chainId": "eip155:280:0x43158f45b5EbD7b1179130130DF00393928C2691",
    "createdAt": "2024-01-12T14:53:02.653457Z",
    "externalId": "27c9aa3b-f12d-43b9-8e91-5264ccc998de",
    "id": "04c5851c-4386-5bd9-9f89-08312f8aee3a",
    "status": "active",
    "updatedAt": "2024-01-12T15:05:32.166028Z"
}
```

Once the customer has been created and successfully verified, we can proceed to setting up accounts and cards.


# Account Creation

{% hint style="info" %}
This step assumes you have successfully created a new customer record and verified it using [Customer Creation](/developer-guides/step-by-step-guide/customer-creation)and [Customer Verification](/developer-guides/step-by-step-guide/customer-verification) steps of the guide
{% endhint %}

Now that you have created and verified a first customer record, you are able to onboard the customer on OffBlocks accounts. To do so, you need to use `/customers/{customerId}/account` `POST` request.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/customers/{customerId}/accounts" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Let's say we want to open a new card account (the only option for the time being) that's using Euro as a base currency (we don't support multi-currency accounts just yet):

```json
{
  "type": "CARD_ACCOUNT",
  "currencies": [
    "EUR"
  ]
}
```

If everything was successful, you will receive a response with status `201 (Created)` which looks similar to the following:

<pre class="language-json"><code class="lang-json"><strong>{
</strong>    "challenge": "NWRmMzU5ZjAtZDI5OS00MGExLTgwMjMtMTdjNzdhNTAxNDRj",
    "createdAt": "2024-01-12T15:07:17.358440283Z",
    "currencies": [
        "EUR"
    ],
    "customerId": "04c5851c-4386-5bd9-9f89-08312f8aee3a",
    "id": "ae61c362-4136-49af-8e01-544457fe265b",
    "status": "initiating",
    "type": "card_account",
    "updatedAt": "2024-01-12T15:07:17.358440283Z"
}
</code></pre>

If everything went well, you will receive an account challenge to sign as a base64 encoded string. This is required to proceed to deployment stage.  Signatures are not currently verified in our Sandbox environment, however they are still required to be submitted.

In order to submit a signed challenge, use a `POST` request to `/accounts/{accountId}/challenge/sign` endpoint.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/challenge/sign" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

```json
{
  "ownerId": "eip155:280:0x43158f45b5EbD7b1179130131DF00393928C2691",
  "challenge": "NWRmMzU5ZjAtZDI5OS00MGExLTgwMjMtMTdjNzdhNTAxNDRj",
  "signature": "c2lnbmF0dXJl"
}
```

Please, not that the signature also must be a base64 encoded string. If signature is verified, you will receive an empty response with status code `204 (No Content)`.

If, for any reason, the original challenge is lost or cannot be signed immediately, you can fetch it again using a dedicated endpoint. If we encounter an error during signature validation via `/sign` endpoint, a new challenge will be issued for you to retrieve.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/challenge" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Once the challenge has been successfully signed, we will proceed to deployment stage. The account will be automatically deployed and activated, which you can verify by making a `GET` request to `/accounts/{accountId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

```json
{
    "chainId": "eip155:280:0x18102C007e4B4FBED74105E8D1e4FF4545Dbed8f",
    "createdAt": "2024-01-12T15:07:17.35844Z",
    "currencies": [
        "EUR"
    ],
    "customerId": "04c5851c-4386-5bd9-9f89-08312f8aee3a",
    "id": "ae61c362-4136-49af-8e01-544457fe265b",
    "status": "active",
    "type": "card_account",
    "updatedAt": "2024-01-12T15:09:31.118452Z"
}
```

From there we can proceed to issuing cards for the account.


# Card Issuance

{% hint style="info" %}
This step assumes you have successfully created a new account record using [Account Creation](/developer-guides/step-by-step-guide/account-creation)step of the guide
{% endhint %}

Now that you have created a customer account, you are able to issue cards for the account. To do so, you need to use `/accounts/{accountId}/card` `POST` request.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/accounts/{accountId}/cards" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Let's say we want to open a new virtual card (physical cards are not supported at the moment):

```json
{
  "type": "virtual",
  "billingAddress": {
    "firstLine": "1 Test Avenue",
    "city": "London",
    "country": "GB",
    "postCode": "SW1 1AA"
  }
}
```

If everything was successful, you will receive a response with status `201 (Created)` which looks similar to the following:

```json
{
    "accountId": "ae61c362-4136-49af-8e01-544457fe265b",
    "billingAddress": {
        "city": "London",
        "country": "GB",
        "firstLine": "1 Test Avenue",
        "postCode": "SW1 1AA"
    },
    "createdAt": "2024-01-12T15:12:26.923705379Z",
    "id": "fc4912aa-cac9-4bb0-a6a7-6ec50bf45879",
    "status": "issuing",
    "type": "virtual",
    "updatedAt": "2024-01-12T15:12:26.923705379Z"
}
```

At this stage, you can use our [Simulator](/developer-guides/api-integration/simulator) to issue or fail a pending card issuance request.

Assuming you would like to issue the card and proceed with next steps, you need to make an empty `PATCH` request to `/simulator/cards/{cardId}/issue`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/cards/{cardId}/issue" method="patch" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

If your request was successful, you will receive an empty response with status `202 (Accepted)`.

In our Sandbox environment the card will be automatically issued and activated, which you can verify by making a `GET` request to `/cards/{cardId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/cards/{cardId}" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

```json
{
    "accountId": "ae61c362-4136-49af-8e01-544457fe265b",
    "billingAddress": {
        "city": "London",
        "country": "GB",
        "firstLine": "1 Test Avenue",
        "postCode": "SW1 1AA"
    },
    "createdAt": "2024-01-12T15:12:26.923705Z",
    "id": "fc4912aa-cac9-4bb0-a6a7-6ec50bf45879",
    "status": "active",
    "type": "virtual",
    "updatedAt": "2024-01-12T15:16:03.983949Z"
}
```

From there we can proceed to simulating card authorisations and transactions.


# Card Authorisations

{% hint style="info" %}
This step assumes you have successfully issued a card using [Card Issuance](/developer-guides/step-by-step-guide/card-issuance)step of this guide
{% endhint %}

Now that you have created a customer account and issued a first card, you are able to simulate card authorisations and process simulated transactions. To do so, you need to use `/simulator/cards/{cardId}/authorisation` `POST` request.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/simulator/cards/{cardId}/authorisation" method="post" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Let's say we want to create an authorisation for €20 and automatically capture the funds:

```json
{
  "amount": "20.0",
  "currency": "EUR",
  "capture": true
}
```

If everything was successful, you will receive a response with status `201 (Created)` which looks similar to the following:

```json
{
    "accountId": "ae61c362-4136-49af-8e01-544457fe265b",
    "amount": "20",
    "cardId": "fc4912aa-cac9-4bb0-a6a7-6ec50bf45879",
    "createdAt": "2024-01-12T15:22:29.451485Z",
    "currency": "EUR",
    "exchangeRate": "1",
    "id": "532a52af-9f68-429b-8945-184773d0889d",
    "merchant": {
        "city": "London",
        "country": "GB",
        "id": "TEST",
        "mcc": 9999,
        "name": "Test Merchant"
    },
    "merchantAmount": "20",
    "merchantCurrency": "EUR",
    "status": "pending",
    "updatedAt": "2024-01-12T15:22:29.905768Z"
}
```

As automatic capture happens asynchronously, you can verify it has been successfully captured and a new transaction created and processed by first making a `GET` request to `/authorisations/{authorisationId}`.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/authorisations/{authorisationId}" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

```json
{
    "accountId": "ae61c362-4136-49af-8e01-544457fe265b",
    "amount": "20",
    "cardId": "fc4912aa-cac9-4bb0-a6a7-6ec50bf45879",
    "createdAt": "2024-01-12T15:22:29.451485Z",
    "currency": "EUR",
    "exchangeRate": "1",
    "id": "532a52af-9f68-429b-8945-184773d0889d",
    "merchant": {
        "city": "London",
        "country": "GB",
        "id": "TEST",
        "mcc": 9999,
        "name": "Test Merchant"
    },
    "merchantAmount": "20",
    "merchantCurrency": "EUR",
    "status": "captured",
    "updatedAt": "2024-01-12T15:22:30.2762Z"
}
```

And then making a `GET` request to `/transactions/{transactionId}` with the same ID.

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/transactions/{transactionId}" method="get" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

```json
{
    "accountId": "ae61c362-4136-49af-8e01-544457fe265b",
    "amount": "20",
    "authorisationId": "532a52af-9f68-429b-8945-184773d0889d",
    "cardId": "fc4912aa-cac9-4bb0-a6a7-6ec50bf45879",
    "chainTransactions": [
        {
            "chainId": "eip155:280:0x0000000000000000000000000000000067517441607a481e825155eaaadcf7e9",
            "createdAt": "2024-01-12T15:22:30.14091932Z"
        }
    ],
    "createdAt": "2024-01-12T15:22:30.268311Z",
    "currency": "EUR",
    "direction": "debit",
    "exchangeRate": "1",
    "fees": [
        {
            "tokenAmount": "0.1",
            "type": "network"
        }
    ],
    "id": "532a52af-9f68-429b-8945-184773d0889d",
    "merchant": {
        "city": "London",
        "country": "GB",
        "id": "TEST",
        "mcc": 9999,
        "name": "Test Merchant"
    },
    "merchantAmount": "20",
    "merchantCurrency": "EUR",
    "status": "processed",
    "token": "eip155:280/erc20:0x6B175474E89094C44Da98b954EedeAC495271d0F",
    "tokenAmount": "20.1",
    "updatedAt": "2024-01-12T15:22:30.479706Z"
}
```


# Webhooks

{% hint style="info" %}
This step is optional and can be done at any point. Active webhook subscription is not a pre-requisite for using OffBlocks API and is engineered for convenient asynchronous processing of events
{% endhint %}

If you want a simple test for webhook notifications, you can set up a catch-all webhook endpoint via <https://webhook.site/>. You will have a unique generated URL you can use to receive notifications from OffBlocks API.

Please, follow the steps from [Webhooks](/developer-guides/api-integration/api-reference/webhooks) to set up and manage webhook notifications. For example, to set up a webhook notification for all entities' updates as a `POST` request to <https://webhook.site/81f3f721-303e-4b3c-899f-57db55aa5262>, make a `PUT` request to `/webhooks` with the following payload:

```json
{
    "callbackUrl": "https://webhook.site/81f3f721-303e-4b3c-899f-57db55aa5262",
    "types": [
        "customer.updated",
        "account.updated",
        "card.updated",
        "authorisation.updated",
        "transaction.updated"
    ]
}
```

{% openapi src="/files/ZuFqAH96VVGgwQAJ2vzc" path="/webhooks" method="put" expanded="true" %}
[offblocks-api-spec\_master\_api.yaml](https://1545901005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTToCQFhAYRMgObGgbSjC%2Fuploads%2FV89o79d3urx40FHQswwW%2Foffblocks-api-spec_master_api.yaml?alt=media\&token=762e0468-4f4f-407e-9281-df4ee98be768)
{% endopenapi %}

Once you create a new customer entity in step [Customer Creation](/developer-guides/step-by-step-guide/customer-creation), you will receive a notification similar to the following:

```json
{
  "chainId": "eip155:300:0x43158f45b5EbD7b1179130130DF00393928C2691",
  "createdAt": "2024-01-24T14:18:37.749861007Z",
  "eventId": "fb2ed25e-11b1-49ce-a703-75f0c2d58c09",
  "externalId": "5fb1479a-0ef4-418e-82b5-0eb76f8a3df9",
  "id": "29ca152b-a06a-5f6d-8f34-06a8f1250141",
  "status": "initiating",
  "updatedAt": "2024-01-24T14:18:37.749861007Z",
  "webhookType": "customer.updated"
}
```


# Wallet Providers

Our main use case is providing wallet app builders with an easy toolkit to onboard their users with card products. Our API allow developers to integrate card issuance and payment capabilities within their Web or Mobile app which in turn lets their customers instantly convert their digital assets to fiat. We partner with biggest card payment networks, Visa and MasterCard, and support digital wallets such as ApplePay and GooglePay which makes cards issued via our platform accepted literally anywhere in the world.


# DeFi Apps

DeFi app builders can benefit from our solution allowing their users to spend their funds, deployed to liquidity pools or yield farms, directly via card payments avoiding extra transactions and associated costs.


# Neo-banks and FinTechs

Our platform offers valuable opportunities for FinTech solutions and neo-banks. It enables their users to access traditional banking products with the assurance of complete backing from a decentralised web3 infrastructure. This approach places control back in the hands of users and fosters the growth of ecosystems by prioritising security and trust as the foundational elements of your technology.


