# Create Account
Source: https://docs.otim.com/api-reference/create-account
POST /accounts
Creates a new Account under an existing Entity. The Account is automatically provisioned on your Platform's default chain. If auto-earn is enabled in your Platform Settings, the Account will be configured to earn yield on idle balances.
# Create Counterparty
Source: https://docs.otim.com/api-reference/create-counterparty
POST /counterparties
Creates a new Counterparty under an existing Entity. A Counterparty represents an external party that funds can be sent to or received from. Counterparties can be either wallet addresses or bank accounts.
# Create Entity
Source: https://docs.otim.com/api-reference/create-entity
POST /entities
Creates a new Entity and initiates the verification flow. The response includes links that the Entity can use to complete identity verification (KYC for individuals, KYB for businesses) and accept the terms of service.
# Create IBAN
Source: https://docs.otim.com/api-reference/create-iban
POST /ibans
Issues a new IBAN for a verified Entity. The IBAN is linked to an Account, and any fiat deposited to it will be automatically converted and routed into that Account. The Entity must have completed verification (KYC/KYB) before an IBAN can be issued.
# Create Transfer
Source: https://docs.otim.com/api-reference/create-transfer
POST /transfers
Creates a transfer from an Account. You can send funds to another Account on your Platform (book transfer) or to an external Counterparty (on-chain or wire transfer). Provide either `destinationAccountId` or `counterpartyId`, but not both.
# Delete Counterparty
Source: https://docs.otim.com/api-reference/delete-counterparty
DELETE /counterparties/{id}
Permanently deletes a Counterparty. This cannot be undone. Any pending transfers to this Counterparty will fail.
# Delete IBAN
Source: https://docs.otim.com/api-reference/delete-iban
DELETE /ibans/{id}
Deactivates and deletes an IBAN. Any future deposits to this IBAN will be rejected. This cannot be undone.
# Get Account
Source: https://docs.otim.com/api-reference/get-account
GET /accounts/{id}
Returns the details of a single Account by ID, including current balances and auto-earn status.
# Get Counterparty
Source: https://docs.otim.com/api-reference/get-counterparty
GET /counterparties/{id}
Returns the details of a single Counterparty by ID.
# Get Entity
Source: https://docs.otim.com/api-reference/get-entity
GET /entities/{id}
Returns the details of a single Entity, including their current verification status. Use this endpoint to check whether an Entity has completed KYC/KYB and is eligible for fiat capabilities like IBANs.
# Get IBAN
Source: https://docs.otim.com/api-reference/get-iban
GET /ibans/{id}
Returns the details of a single IBAN by ID.
# Get Platform Settings
Source: https://docs.otim.com/api-reference/get-platform-settings
GET /platform/settings
Returns the current settings for your Platform, including auto-earn configuration, default chain, and webhook URL.
# Get Transfer
Source: https://docs.otim.com/api-reference/get-transfer
GET /transfers/{id}
Returns the details of a single transfer by ID.
# List Accounts
Source: https://docs.otim.com/api-reference/list-accounts
GET /accounts
Returns a paginated list of Accounts on your Platform.
# List Counterparties
Source: https://docs.otim.com/api-reference/list-counterparties
GET /counterparties
Returns a paginated list of Counterparties on your Platform.
# List Entities
Source: https://docs.otim.com/api-reference/list-entities
GET /entities
Returns a paginated list of Entities on your Platform.
# List IBANs
Source: https://docs.otim.com/api-reference/list-ibans
GET /ibans
Returns a paginated list of IBANs on your Platform.
# List Transfers
Source: https://docs.otim.com/api-reference/list-transfers
GET /transfers
Returns a paginated list of transfers on your Platform.
# Update Account
Source: https://docs.otim.com/api-reference/update-account
PATCH /accounts/{id}
Updates an Account's display name or metadata. Only the fields included in the request body will be changed.
# Update Counterparty
Source: https://docs.otim.com/api-reference/update-counterparty
PATCH /counterparties/{id}
Updates a Counterparty's display name.
# Update Platform Settings
Source: https://docs.otim.com/api-reference/update-platform-settings
PATCH /platform/settings
Updates your Platform settings. Any fields not included in the request body will remain unchanged.
# Account
Source: https://docs.otim.com/core-objects/account
Holds, routes, and earns yield on funds.
An Account is the object on Otim that holds, routes, and earns yield on funds. Every Account belongs to a Platform and has a wallet address on a specific blockchain network. Accounts are the core primitive you interact with when moving money through Otim.
Accounts serve three main purposes:
1. **Holding funds.** Each Account maintains real-time balances (available, pending, locked, and holding) so you always know exactly where your money is.
2. **Routing funds.** Accounts are the source and destination for transfers, orchestrations, and automated instructions. When you set up an Otim Instruction to transfer ETH every week, that instruction runs against an Account. When an Orchestrator settles a cross-chain payment, the funds land in an Account.
3. **Earning yield.** Accounts can be configured to automatically earn yield on idle funds, so capital that would otherwise sit dormant is put to work.
## Linking to an Entity
Accounts can optionally be linked to an Entity. This is not required for basic functionality. An Account without an Entity can still hold funds, execute instructions, and participate in orchestrations. However, linking an Entity unlocks additional capabilities like fiat deposit rails (via IBANs) and external transfers (via Counterparties).
A single Entity can own multiple Accounts. This is useful when you want to separate funds by purpose (e.g., a treasury Account and an operations Account) while keeping them under the same verified identity.
# Counterparty
Source: https://docs.otim.com/core-objects/counterparty
An external wallet or bank account for outbound transfers.
A Counterparty represents an external destination that funds can be sent to or from Otim. This can be an external wallet address on any supported chain or a traditional bank account. Counterparties are how you model the "other side" of an outbound transfer.
Every Counterparty belongs to an Entity. This creates a clear ownership chain: your Platform owns the Entity, the Entity has been verified through KYC/KYB, and the Counterparty is a known destination tied to that verified identity. This structure ensures that outbound transfers always have a traceable path from source to destination.
## Types of Counterparties
Counterparties come in two forms:
* **Wallet**: An external blockchain address. Use this when you need to send tokens to an address outside of your Platform's Accounts.
* **Bank account**: A traditional bank account with routing and account details. Use this when you need to send fiat to an external bank.
## When to use Counterparties
Counterparties are required whenever you want to move funds outside of your Platform. Transfers between your own Accounts do not need a Counterparty since both sides are already within your Platform. But as soon as the destination is external, you need a Counterparty to represent it.
A single Entity can have multiple Counterparties. For example, a verified business Entity might have one Counterparty for a partner's wallet address and another for their operating bank account.
# Entity
Source: https://docs.otim.com/core-objects/entity
A verified person or business.
An Entity represents a verified person or business on Otim. Entities are created through a KYC (Know Your Customer) or KYB (Know Your Business) verification process and are scoped to the Platform that created them.
Entities exist because certain financial operations require a verified identity behind them. While Accounts can function on their own for on-chain operations, an Entity is what bridges the gap between on-chain and off-chain finance.
## What verification unlocks
Before an Entity is verified, it is essentially a placeholder with a pending status. Once verification is complete, the Entity unlocks three key capabilities:
1. **Fiat deposit rails.** A verified Entity can be issued an IBAN, which allows it to receive fiat deposits that automatically route into a linked Account.
2. **External transfer destinations.** A verified Entity can create Counterparties, which are external wallet addresses or bank accounts that funds can be sent to.
3. **KYC-gated flows.** Some operations on Otim require a verified identity by design. Having a verified Entity attached to an Account ensures those flows are available.
## Entity types
An Entity can represent either an individual person or a business. The verification process differs depending on the type:
* **Individual**: Goes through KYC, which typically requires personal identification documents.
* **Business**: Goes through KYB, which typically requires business registration documents and beneficial ownership information.
## Relationship to Accounts
An Entity can own multiple Accounts. This lets you model real-world structures where a single person or business manages several financial accounts for different purposes. Linking an Entity to an Account is optional, but once linked, that Account inherits access to the Entity's fiat rails and counterparties.
# IBAN
Source: https://docs.otim.com/core-objects/iban
A fiat deposit rail issued to a verified Entity.
An IBAN is a fiat deposit rail that allows a verified Entity to receive traditional bank transfers into Otim. When someone sends fiat to the IBAN, the funds are automatically converted and routed into an Account linked to that Entity.
IBANs are the on-ramp from traditional banking into the Otim platform. They bridge the gap between fiat and on-chain finance by giving each verified Entity a dedicated bank account number that external parties can send money to. Otim supports every G7 currency and many more.
## How it works
1. A verified Entity is issued an IBAN.
2. The IBAN is linked to one of the Entity's Accounts.
3. When fiat arrives at the IBAN, Otim automatically routes the funds into that Account.
From there, the funds are available for any operation you would normally perform with an Account: transfers, orchestrations, instructions, or simply holding and earning yield.
## Requirements
Only verified Entities can receive IBANs. This means the Entity must have completed KYC (for individuals) or KYB (for businesses) before an IBAN can be issued. This requirement exists because fiat deposit rails are subject to regulatory compliance, and a verified identity is necessary to operate them.
## When to use IBANs
IBANs are useful whenever you need to accept fiat payments from external parties who use traditional banking. Common scenarios include:
* Accepting customer payments via bank transfer
* Receiving payroll or vendor payments into an Otim Account
* On-ramping fiat into Otim for cross-chain settlement via Orchestrators
# Platform
Source: https://docs.otim.com/core-objects/platform
Top-level container for all objects on Otim.
A Platform represents your company on Otim. It is the top-level container that scopes every other object in the system. Depending on your use case, you will create accounts, entities, and other resources under your Platform to model your financial operations.
# Authentication
Source: https://docs.otim.com/docs/api/authentication
All API requests must include a valid API key in the `Authorization` header using the Bearer scheme.
```bash theme={null}
curl https://api.otim.com/v0/accounts \
-H "Authorization: Bearer sk_..."
```
## API keys
API keys are created in the Developer section of your dashboard. Each key is scoped to your Platform and can be independently labeled and revoked.
Keys are prefixed with `sk_` and should be treated as secrets. Do not expose them in client-side code, public repositories, or logs.
## Environments
| Environment | Base URL | Description |
| ----------- | --------------------------------- | ----------------------------------------------------- |
| Sandbox | `https://sandbox.api.otim.com/v0` | For development and testing. No real funds are moved. |
| Production | `https://api.otim.com/v0` | For live operations with real funds. |
Sandbox and production use separate API keys. You can create and manage keys for each environment independently from your dashboard.
## Error responses
Requests with a missing or invalid API key return a `401 Unauthorized` response:
```json theme={null}
{
"error": "Invalid API key.",
"code": "unauthorized"
}
```
# Idempotency
Source: https://docs.otim.com/docs/api/idempotency
To safely retry requests without creating duplicate resources, you can include an `Idempotency-Key` header on any POST request.
```bash theme={null}
curl -X POST https://api.otim.com/v0/transfers \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: your-unique-key-here" \
-d '{
"sourceAccountId": "acc_01JA1B2C3D4E5F6G7H8J9K0L",
"destinationAccountId": "acc_02XY9Z8W7V6U5T4S3R2Q1P0N",
"amount": "100.00"
}'
```
## How it works
* If a request with the same `Idempotency-Key` has already been processed, the API returns the original response without executing the operation again.
* Idempotency keys are scoped to your Platform and expire after 24 hours.
* Keys can be any unique string up to 256 characters. UUIDs are a good choice.
## When to use idempotency keys
Use idempotency keys on any request where a retry could cause unintended side effects, especially:
* **Creating transfers** to prevent duplicate payments.
* **Creating Accounts or Entities** to prevent duplicate resources if a network timeout occurs.
GET, PATCH, and DELETE requests are naturally idempotent and do not require an idempotency key.
# Pagination
Source: https://docs.otim.com/docs/api/pagination
All list endpoints return paginated results using cursor-based pagination. This ensures consistent results even when new items are created between requests.
## Response format
Every list endpoint returns the same pagination structure:
```json theme={null}
{
"data": [],
"hasMore": true,
"nextCursor": "abc123"
}
```
| Field | Type | Description |
| ------------ | -------------- | ------------------------------------------------------------------------- |
| `data` | array | The list of items for the current page. |
| `hasMore` | boolean | Whether there are more items after this page. |
| `nextCursor` | string or null | The cursor to use for the next page. `null` when there are no more items. |
## Fetching the next page
To fetch the next page, pass the `nextCursor` value as the `cursor` query parameter:
```bash theme={null}
# First page
curl https://api.otim.com/v0/accounts?limit=10 \
-H "Authorization: Bearer sk_..."
# Next page
curl https://api.otim.com/v0/accounts?limit=10&cursor=abc123 \
-H "Authorization: Bearer sk_..."
```
Continue paginating until `hasMore` is `false`.
## Parameters
| Parameter | Default | Max | Description |
| --------- | ------- | --- | ----------------------------------------------------- |
| `limit` | 50 | 100 | Number of items to return per page. |
| `cursor` | - | - | Cursor from a previous response's `nextCursor` field. |
# Sandbox & Testing
Source: https://docs.otim.com/docs/api/sandbox
The sandbox environment lets you build and test your integration without moving real funds. It mirrors the production API with a few differences designed to make testing easier.
## Base URL
```
https://sandbox.api.otim.com/v0
```
Use sandbox API keys (created separately in your dashboard) to authenticate requests to this environment.
## What works in sandbox
The sandbox supports the full API surface. You can create Entities, Accounts, Counterparties, IBANs, and Transfers just like in production.
Key differences:
* **No real funds are moved.** Transfers are simulated and complete instantly.
* **Verification is auto-approved.** Entities created in sandbox are automatically approved for KYC and ToS, so you can test the full flow without waiting for manual review.
* **IBANs accept simulated deposits.** You can trigger test deposits to sandbox IBANs from your dashboard.
* **Webhooks fire normally.** If you have a webhook URL configured, sandbox events will be delivered to it. Use this to test your webhook handler.
## Test data
Sandbox Accounts start with a zero balance. You can fund them by triggering a simulated deposit from your dashboard or by creating a test IBAN and sending a simulated fiat deposit.
## Moving to production
When you are ready to go live:
1. Create production API keys in your dashboard.
2. Switch your base URL to `https://api.otim.com/v0`.
3. Ensure your Entities complete real KYC/KYB verification before issuing IBANs or processing live transfers.
# Transfer Types
Source: https://docs.otim.com/docs/api/transfer-types
When you create a transfer, Otim automatically determines the transfer type based on the destination you provide.
## Book transfer
A book transfer moves funds between two Accounts on the same Platform. These settle instantly with no fees.
```json theme={null}
{
"sourceAccountId": "acc_01...",
"destinationAccountId": "acc_02...",
"amount": "100.00"
}
```
Provide `destinationAccountId` to create a book transfer.
## Wire transfer
A wire transfer sends funds to an external bank account via a Counterparty. These are subject to standard processing times for the destination banking network.
```json theme={null}
{
"sourceAccountId": "acc_01...",
"counterpartyId": "cpty_01...",
"amount": "500.00",
"currency": "USD"
}
```
Provide a `counterpartyId` that has type `bank_account` to create a wire transfer.
## On-chain transfer
An on-chain transfer sends funds to an external wallet via a Counterparty. These settle once the transaction is confirmed on the destination chain.
```json theme={null}
{
"sourceAccountId": "acc_01...",
"counterpartyId": "cpty_02...",
"amount": "250.00"
}
```
Provide a `counterpartyId` that has type `wallet` to create an on-chain transfer.
## Transfer statuses
| Status | Description |
| ------------ | --------------------------------------------------------------------- |
| `pending` | The transfer has been created and is queued for processing. |
| `processing` | The transfer is actively being processed. |
| `completed` | The transfer has settled successfully. |
| `failed` | The transfer failed. Check the transfer details for more information. |
| `cancelled` | The transfer was cancelled before it was processed. |
# Webhooks
Source: https://docs.otim.com/docs/api/webhooks
Webhooks allow you to receive real-time notifications when events happen on your Platform. Instead of polling the API, you can configure a URL that Otim will send POST requests to whenever a relevant event occurs.
## Setup
Configure your webhook URL via the [Update Platform Settings](/api-reference/update-platform-settings) endpoint:
```bash theme={null}
curl -X PATCH https://api.otim.com/v0/platform/settings \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://yourapp.com/webhooks/otim"
}'
```
Set `webhookUrl` to `null` to disable webhooks.
## Payload format
All webhook payloads follow the same structure:
```json theme={null}
{
"id": "evt_01JA1B2C3D4E5F6G7H8J9K0L",
"type": "transfer.completed",
"createdAt": "2025-01-15T12:00:00Z",
"data": { }
}
```
| Field | Type | Description |
| ----------- | ------ | ---------------------------------------------- |
| `id` | string | Unique identifier for the event. |
| `type` | string | The event type. |
| `createdAt` | string | ISO 8601 timestamp of when the event occurred. |
| `data` | object | The full object associated with the event. |
## Event types
### Transfers
| Event | Description |
| --------------------- | ---------------------------------------- |
| `transfer.created` | A new transfer has been initiated. |
| `transfer.processing` | The transfer is being processed. |
| `transfer.completed` | The transfer has completed successfully. |
| `transfer.failed` | The transfer has failed. |
### Entities
| Event | Description |
| --------------------- | ----------------------------------------------- |
| `entity.kyc.approved` | An Entity has passed identity verification. |
| `entity.kyc.rejected` | An Entity's identity verification was rejected. |
| `entity.tos.approved` | An Entity has accepted the terms of service. |
### Accounts
| Event | Description |
| ----------------- | ----------------------------------- |
| `account.created` | A new Account has been provisioned. |
### IBANs
| Event | Description |
| ----------------------- | ----------------------------------------------------------------------------- |
| `iban.deposit.received` | A fiat deposit has been received at an IBAN and routed to the linked Account. |
## Responding to webhooks
Your endpoint should return a `200` status code within 10 seconds. If Otim does not receive a `200` response, the webhook will be retried up to 5 times with exponential backoff.
## Verifying signatures
Each webhook request includes a `X-Otim-Signature` header containing an HMAC-SHA256 signature of the request body. Verify this signature to ensure the request came from Otim.
```javascript theme={null}
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
```
Your webhook signing secret is available in the Developer section of your dashboard.
# Actions & Instructions
Source: https://docs.otim.com/docs/concepts/actions-instructions
Actions and Instructions work together as the building blocks of Otim's execution engine. Actions are smart contracts that define what can be executed, while Instructions specify how and when these Actions should be executed. Think of Actions as the "what" and Instructions as the "when" and "how" of each automated operation.
To give a simple example, you might create an Instruction to transfer 0.1 ETH to your friend Alice every week. Let's deconstruct this sentence:
* "transfer" here refers to executing the `Transfer` Action contract
* "0.1 ETH" and "to Alice" refer to specific `Transfer` Action arguments
* "every week" refers to specific Instruction scheduling parameters
# Delegation & EIP-7702
Source: https://docs.otim.com/docs/concepts/delegation
EIP-7702 "delegation" is the process that allows users to enable Otim's smart features for their Externally Owned Accounts (EOAs). Users can easily delegate to Otim by following the onboarding flow in the App. During this process, users can decide to upgrade their EOA on a specific blockchain or for all EVM blockchains that implement EIP-7702.
## What does this mean?
In this context, to "delegate" means to authorize a smart contract to act on your behalf. After you delegate to Otim, your EOA can be called as if it were the Otim core smart contract. This enables all sorts of automation functionality since Otim can now interact with your EOA programmatically.
## How does it work?
At a high level, you can delegate to Otim by signing a specific delegation message and sending this signed message to Otim. Once Otim receives and parses the signed message, it will send a transaction on-chain to perform the delegation. The Otim App's onboarding flow is designed to walk you through this process and explain more details along the way.
## EIP-7702
[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) is a new transaction type that allows EOAs to set persistent runtime bytecode in their account. This enables Ethereum accounts to delegate execution authority to a trusted smart contract while maintaining full control of their assets. At its core, EIP-7702 acts as a secure connector that allows EOAs to tap into smart contract functionality without changing their fundamental security model. This creates a foundation for building sophisticated account features while preserving the simplicity and security of traditional Ethereum accounts.
During an EIP-7702 transaction, a "delegation designator" of the form `0xef0100 || delegate_address` is set in the `code` field of the signing EOA. Subsequently, all code-executing operations called on the EOA will load the runtime bytecode of the contract deployed at `delegate_address` and execute in the context of the EOA.
The delegation process itself is straightforward: users first authorize a specific smart contract by signing an EIP-7702 authorization. This authorization is then submitted to the network through an EIP-7702 transaction, which establishes the delegation relationship. Once delegated, the account can utilize all the functionality of the authorized smart contract while maintaining its sovereignty. Users can revoke this delegation at any time by submitting a transaction that clears the delegation, effectively removing the smart contract's ability to act on behalf of the account.
# Deployed Addresses
Source: https://docs.otim.com/docs/developers/deployed-addresses
## Core Contracts
| Name | Address |
| :------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://etherscan.io/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** |
| `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://etherscan.io/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** |
| `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://etherscan.io/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** |
| `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://etherscan.io/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** |
| `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://etherscan.io/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** |
| `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://etherscan.io/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://etherscan.io/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** |
| `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://etherscan.io/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** |
| `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://etherscan.io/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** |
| `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://etherscan.io/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** |
| `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://etherscan.io/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** |
| `UniswapV3ExactInputAction` | **[0xd8BE0f31BbF564C584599069ED3cb93b9b2264c7](https://etherscan.io/address/0xd8BE0f31BbF564C584599069ED3cb93b9b2264c7)** |
## Core Contracts
| Name | Address |
| :------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://basescan.org/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** |
| `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://basescan.org/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** |
| `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://basescan.org/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** |
| `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://basescan.org/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** |
| `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://basescan.org/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** |
| `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://basescan.org/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://basescan.org/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** |
| `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://basescan.org/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** |
| `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://basescan.org/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** |
| `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://basescan.org/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** |
| `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://basescan.org/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** |
| `UniswapV3ExactInputAction` | **[0x8a3E70F61288047d016d9975975dFEfB187Ad6cB](https://basescan.org/address/0x8a3E70F61288047d016d9975975dFEfB187Ad6cB)** |
## Core Contracts
| Name | Address |
| :------------------- | :----------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://arbiscan.io/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** |
| `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://arbiscan.io/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** |
| `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://arbiscan.io/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** |
| `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://arbiscan.io/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** |
| `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://arbiscan.io/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** |
| `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://arbiscan.io/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://arbiscan.io/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** |
| `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://arbiscan.io/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** |
| `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://arbiscan.io/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** |
| `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://arbiscan.io/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** |
| `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://arbiscan.io/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** |
| `UniswapV3ExactInputAction` | **[0x6b2Cd8195204d43D7113fE23e3dC5f1a30862Ff3](https://arbiscan.io/address/0x6b2Cd8195204d43D7113fE23e3dC5f1a30862Ff3)** |
## Core Contracts
| Name | Address |
| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://optimistic.etherscan.io/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** |
| `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://optimistic.etherscan.io/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** |
| `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://optimistic.etherscan.io/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** |
| `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://optimistic.etherscan.io/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** |
| `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://optimistic.etherscan.io/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** |
| `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://optimistic.etherscan.io/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://optimistic.etherscan.io/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** |
| `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://optimistic.etherscan.io/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** |
| `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://optimistic.etherscan.io/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** |
| `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://optimistic.etherscan.io/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** |
| `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://optimistic.etherscan.io/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** |
| `UniswapV3ExactInputAction` | **[0xDeFAa4B69440dd569d295E60241667af8da5C777](https://optimistic.etherscan.io/address/0xDeFAa4B69440dd569d295E60241667af8da5C777)** |
## Core Contracts
| Name | Address |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia.etherscan.io/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** |
| `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia.etherscan.io/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** |
| `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia.etherscan.io/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** |
| `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia.etherscan.io/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** |
| `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia.etherscan.io/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** |
| `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia.etherscan.io/address/0xc7536a1e8205C139d25165F6305e20299b792511)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia.etherscan.io/address/0x1f719754FC21E58b628E02659896309354888058)** |
| `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia.etherscan.io/address/0x355A1dA513d010fdD404E138807ea75731d08133)** |
| `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia.etherscan.io/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** |
| `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia.etherscan.io/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** |
| `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia.etherscan.io/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** |
| `SweepCCTPAction` | **[0xa4B51D22e75783a0e52036d574b53A2101Ef8652](https://sepolia.etherscan.io/address/0xa4B51D22e75783a0e52036d574b53A2101Ef8652)** |
| `SweepUniswapV3Action` | **[0xE68Cae39ce20B1670dc2Ca17FcA63D0EafB27701](https://sepolia.etherscan.io/address/0xE68Cae39ce20B1670dc2Ca17FcA63D0EafB27701)** |
| `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia.etherscan.io/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** |
| `TransferCCTPAction` | **[0x2CA6cAA0B4F5653b1Aa4B4922A3fF34f868112F3](https://sepolia.etherscan.io/address/0x2CA6cAA0B4F5653b1Aa4B4922A3fF34f868112F3)** |
| `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia.etherscan.io/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** |
| `UniswapV3ExactInputAction` | **[0x38C4623A081586d873137995a77440B801F5CD7c](https://sepolia.etherscan.io/address/0x38C4623A081586d873137995a77440B801F5CD7c)** |
## Core Contracts
| Name | Address |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia.basescan.org/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** |
| `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia.basescan.org/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** |
| `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia.basescan.org/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** |
| `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia.basescan.org/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** |
| `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia.basescan.org/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** |
| `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia.basescan.org/address/0xc7536a1e8205C139d25165F6305e20299b792511)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia.basescan.org/address/0x1f719754FC21E58b628E02659896309354888058)** |
| `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia.basescan.org/address/0x355A1dA513d010fdD404E138807ea75731d08133)** |
| `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia.basescan.org/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** |
| `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia.basescan.org/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** |
| `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia.basescan.org/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** |
| `SweepCCTPAction` | **[0x89972bD81F8081b793D3E4Ce2D092495415016D8](https://sepolia.basescan.org/address/0x89972bD81F8081b793D3E4Ce2D092495415016D8)** |
| `SweepUniswapV3Action` | **[0x1E1A662d44d605e524797f2cD155320a0a41051b](https://sepolia.basescan.org/address/0x1E1A662d44d605e524797f2cD155320a0a41051b)** |
| `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia.basescan.org/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** |
| `TransferCCTPAction` | **[0x60960EE9957f4280F3A4e5997AfdAf59AEDF94Ec](https://sepolia.basescan.org/address/0x60960EE9957f4280F3A4e5997AfdAf59AEDF94Ec)** |
| `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia.basescan.org/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** |
| `UniswapV3ExactInputAction` | **[0x701e90a513c19bb70e9b1bc885Fe65675d057921](https://sepolia.basescan.org/address/0x701e90a513c19bb70e9b1bc885Fe65675d057921)** |
## Core Contracts
| Name | Address |
| :------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia.arbiscan.io/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** |
| `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia.arbiscan.io/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** |
| `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia.arbiscan.io/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** |
| `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia.arbiscan.io/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** |
| `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia.arbiscan.io/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** |
| `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia.arbiscan.io/address/0xc7536a1e8205C139d25165F6305e20299b792511)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia.arbiscan.io/address/0x1f719754FC21E58b628E02659896309354888058)** |
| `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia.arbiscan.io/address/0x355A1dA513d010fdD404E138807ea75731d08133)** |
| `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia.arbiscan.io/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** |
| `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia.arbiscan.io/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** |
| `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia.arbiscan.io/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** |
| `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia.arbiscan.io/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** |
| `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia.arbiscan.io/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** |
| `UniswapV3ExactInputAction` | **[0x701e90a513c19bb70e9b1bc885Fe65675d057921](https://sepolia.arbiscan.io/address/0x701e90a513c19bb70e9b1bc885Fe65675d057921)** |
## Core Contracts
| Name | Address |
| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia-optimism.etherscan.io/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** |
| `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia-optimism.etherscan.io/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** |
| `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia-optimism.etherscan.io/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** |
| `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia-optimism.etherscan.io/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** |
| `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia-optimism.etherscan.io/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** |
| `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia-optimism.etherscan.io/address/0xc7536a1e8205C139d25165F6305e20299b792511)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia-optimism.etherscan.io/address/0x1f719754FC21E58b628E02659896309354888058)** |
| `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia-optimism.etherscan.io/address/0x355A1dA513d010fdD404E138807ea75731d08133)** |
| `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia-optimism.etherscan.io/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** |
| `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia-optimism.etherscan.io/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** |
| `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia-optimism.etherscan.io/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** |
| `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia-optimism.etherscan.io/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** |
| `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia-optimism.etherscan.io/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** |
| `UniswapV3ExactInputAction` | **[0x701e90a513c19bb70e9b1bc885Fe65675d057921](https://sepolia-optimism.etherscan.io/address/0x701e90a513c19bb70e9b1bc885Fe65675d057921)** |
## Core Contracts
| Name | Address |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://explorer-host.pecorino.signet.sh/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** |
| `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://explorer-host.pecorino.signet.sh/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** |
| `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://explorer-host.pecorino.signet.sh/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** |
| `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://explorer-host.pecorino.signet.sh/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** |
| `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://explorer-host.pecorino.signet.sh/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** |
| `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://explorer-host.pecorino.signet.sh/address/0xc7536a1e8205C139d25165F6305e20299b792511)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://explorer-host.pecorino.signet.sh/address/0x1f719754FC21E58b628E02659896309354888058)** |
| `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://explorer-host.pecorino.signet.sh/address/0x355A1dA513d010fdD404E138807ea75731d08133)** |
| `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://explorer-host.pecorino.signet.sh/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** |
| `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://explorer-host.pecorino.signet.sh/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** |
| `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://explorer-host.pecorino.signet.sh/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** |
| `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://explorer-host.pecorino.signet.sh/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** |
| `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://explorer-host.pecorino.signet.sh/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** |
## Core Contracts
| Name | Address |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://explorer.pecorino.signet.sh/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** |
| `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://explorer.pecorino.signet.sh/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** |
| `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://explorer.pecorino.signet.sh/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** |
| `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://explorer.pecorino.signet.sh/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** |
| `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://explorer.pecorino.signet.sh/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** |
| `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://explorer.pecorino.signet.sh/address/0xc7536a1e8205C139d25165F6305e20299b792511)** |
## Action Contracts
| Name | Address |
| :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://explorer.pecorino.signet.sh/address/0x1f719754FC21E58b628E02659896309354888058)** |
| `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://explorer.pecorino.signet.sh/address/0x355A1dA513d010fdD404E138807ea75731d08133)** |
| `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://explorer.pecorino.signet.sh/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** |
| `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://explorer.pecorino.signet.sh/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** |
| `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://explorer.pecorino.signet.sh/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** |
| `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://explorer.pecorino.signet.sh/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** |
| `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://explorer.pecorino.signet.sh/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** |
# Orchestrators
Source: https://docs.otim.com/docs/developers/orchestrators/overview
Otim Orchestrators are atomic routers that turn complex, multi-step stablecoin routing into a single transfer. No more juggling swaps, transfers, or async calls across chains and tokens. Simply send any asset on any chain to an Orchestrator address, and always get the result you expect.
Think of them like **webhooks for money**: they use the Otim OS under the hood to automatically route and transform any incoming asset based on the desired settlement state.
### Why Use Orchestrators?
* **Single API Call**: Define your desired money flow and generate an Orchestrator address via a single API call
* **Automatic Routing**: Send any digital asset to the Orchestrator and watch it route and transform **on autopilot**
* **Flexible Configuration**: Configure Orchestrators for one-time or persistent usage
* **Trustless & Non-Custodial**: Every Orchestrator is **non-custodial** with **trustless execution** and **built-in compliance**
## Use Cases
### Cross-Chain Payment Settlement
Accept payments in any asset on any chain, and automatically:
* Bridge assets across chains
* Convert to your preferred settlement currency
* Route to any financial account
### Yield Vault Deposits
Send any asset to deposit into a yield vault that only accepts USDC. The Orchestrator automatically:
* Accepts multiple asset types (ETH, USDT, DAI, etc.)
* Swaps them to USDC if needed
* Deposits the USDC into the yield vault
### Bank Conversions & Fiat Orchestration
Orchestrate complex fiat settlement flows across multiple local currencies, tokens, and banking partners. The Orchestrator handles:
* Converting any digital asset to fiat across all kinds of local currencies (USD, EUR, etc.) and acceptable token formats
* Accepting assets from any chain and automatically convert them to bank-acceptable formats
* Settling to traditional banking partners
* Orchestrating assets immediately when onramped assets land on an Orchestrator
***
Want to get started? Head over to the [SDK](/sdk/overview) section to get going!
# Off-chain
Source: https://docs.otim.com/docs/developers/system-architecture/off-chain/off-chain
Otim's off-chain infrastructure consists of two main components that work alongside the on-chain protocol.
## API
The web API acts as an interface between the Otim App and the rest of the system. While users can view account activity, craft new Instructions, and sign messages entirely in the App, some operations require access to Otim's backend system. The API allows the App to read user data from the database and request the Executor to interact with the Otim protocol.
Check out all API endpoints in the [API Reference](/api-reference/create-account).
## Executor
The Executor is an off-chain agent designed to execute Instructions as soon as their conditions are valid on-chain.
All conditions that need to be met for an Instruction to successfully execute on-chain are strictly defined in the Otim protocol. This means that if the Executor submits an Instruction too early, the transaction will simply revert. The Executor's incentives are clear:
1. **Don't** waste gas trying to submit invalid Instructions
2. **Do** execute Instructions when they become valid in order to collect a fee
# Current Actions
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/action-contracts/current-actions
Below is the first batch of Actions currently available to users, but many more Actions are in development!
| Action | Description |
| :------------------------ | :----------------------------------------------------------------------------- |
| **DeactivateInstruction** | Deactivate an existing instruction to stop its execution. |
| **Refuel** | Refuel a target account when its native token balance falls below a threshold. |
| **RefuelERC20** | Refuel a target account when its ERC20 token balance falls below a threshold. |
| **Sweep** | Move native tokens from a target account when balance exceeds a threshold. |
| **SweepCCTP** | Move CCTP tokens from a target account when balance exceeds a threshold. |
| **SweepERC20** | Move ERC20 tokens from a target account when balance exceeds a threshold. |
| **SweepUniswapV3** | Move tokens via Uniswap V3 swap when balance exceeds a threshold. |
| **Transfer** | Send native tokens to a target account at scheduled intervals. |
| **TransferCCTP** | Send CCTP tokens to a destination domain at scheduled intervals. |
| **TransferERC20** | Send ERC20 tokens to a target account at scheduled intervals. |
| **UniswapV3ExactInput** | Swap tokens using Uniswap V3 pools at scheduled intervals. |
Find all of the above Actions' deployed contract addresses [here](/docs/developers/deployed-addresses).
Reference the 712 hash ABI's for each Action [here](/docs/resources/appendix/action-hash-abis).
# Structure
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/action-contracts/structure
Each Action contract must implement the `IAction` interface. This interface defines two external functions: `argumentsHash` and `execute`
### `argumentsHash`
This function returns the information necessary to construct the signing hash for the Instruction depending on the Action arguments.
### `execute`
The `execute` function contains the actual feature-specific logic and is called during each execution. This function is designed to be lightweight and reusable considering Instructions are designed to be executed many times.
# ActionManager
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/action-manager
This is an Otim-operated contract for managing which Actions can be executed through the protocol. Only Actions that are added to this contract can be executed by Otim users. This layer of security is meant to prevent users from accidentally executing buggy or malicious Action contracts. If a bug or exploit is found in one of the active Action contracts, Otim can easily remove the contract from `ActionManager` and deploy a new one. `ActionManager` also includes a kill-switch for the entire protocol in case a bug or exploit is found in Otim's core contracts.
The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/core/ActionManager.sol).
# InstructionStorage
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/instruction-storage
This helper contract holds the execution state of each Instruction for each user. This contract accurately maintains execution state for each Instruction while enforcing strict access control for storage writes. Only EOAs delegated to our specific instance of `OtimDelegate` can write to the storage of this contract and cannot call it directly.
Also, data is "siloed" by the user's address, meaning each user can only alter their own data. This strict access control strategy ensures that all user data is secure and accurate on-chain.
The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/core/InstructionStorage.sol).
# OtimDelegate
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/otim-delegate
This is the main `otim-protocol` smart contract. This is the contract that users will delegate their EOAs to in order to enable Otim's features (also known as the "delegate contract"). It is this contract that actually executes within the context of the user's account. While the user is delegated to this contract, the runtime bytecode *is* the user's account.
`OtimDelegate` contains general Instruction validation and execution logic, as well as deactivation logic. Once a user is delegated to this contract, they can construct and sign Instructions to be executed on their behalf.
The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/OtimDelegate.sol).
# Treasury
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/treasury
This contract holds all fees collected from Instruction executions. Each time an Instruction is executed on behalf of a user, a fee is collected and sent to this `Treasury` contract. To read more on Otim's fee structure, check out the [Fee Structure](/docs/concepts/fee-structure) page.
The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/infrastructure/Treasury.sol).
# Instruction Lifecycle
Source: https://docs.otim.com/docs/developers/system-architecture/protocol/instruction-flows/instruction-lifecycle
Instructions are processed through Otim's core contracts at different points in their lifecycle. Everything interacting with Instructions uses EIP-712 hashes for execution validation. See [How to Construct Instruction EIP-712 Hashes](/docs/resources/appendix/instruction-712-hashes) for more detail.
## Activation
An Instruction's first execution (a.k.a. activation) is special because certain validity conditions are checked at this time that don't need to be checked for subsequent executions. Since Instructions are immutable and designed to be executed more than once, we can get a lot out of the way during activation.
For example, the user's signature is only validated during activation. The scheduling and validity of subsequent executions depend entirely on the Instruction scheduling parameters, the application logic of the Action contract, and the Action arguments. Because of this, users get recurring executions with just one signature.
## Execution
After activation, an Instruction can only be executed when the scheduling parameters and Action application logic allow. The user's signature does not need to be checked for subsequent executions because they have already committed to the execution dynamics defined in the Instruction. This means subsequent executions can be submitted by the Otim Executor in a strictly defined way without the user's ongoing attention and intervention.
## Deactivation
While each Instruction's execution dynamics are strictly defined on-chain, a user may decide to deactivate an Instruction before its natural completion. For example, say Alice wants to automate her rent payments so she creates a simple `Transfer` Instruction that automatically sends \$1000 USDC to her landlord every month for the remainder of her lease. A few months down the line, Alice decides to break her lease and move into a new apartment. In this case, Alice should deactivate her `Transfer` Instruction before its natural end to stop paying her former landlord.
# Getting Started
Source: https://docs.otim.com/docs/getting-started
Create your Platform and make your first API call.
## 1. Request access
Sign up at [otim.com](https://otim.com) and request Platform access. Once approved, you will receive access to your dashboard and sandbox environment.
## 2. Get your API key
Navigate to the Developer section of your dashboard to create an API key. Include it in the `Authorization` header of every request:
```bash theme={null}
curl https://api.otim.com/v0/platform/settings \
-H "Authorization: Bearer sk_..."
```
## 3. Configure your Platform
Set up your Platform settings to configure defaults for your Accounts, like auto-earn and your preferred chain.
```bash theme={null}
curl -X PATCH https://api.otim.com/v0/platform/settings \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"autoEarnEnabled": true,
"autoEarnVault": "aave_usdc",
"defaultChain": "base"
}'
```
## 4. Create an Entity
An [Entity](/core-objects/entity) represents a person or business on your Platform. Creating an Entity kicks off the verification flow.
```bash theme={null}
curl -X POST https://api.otim.com/v0/entities \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"type": "individual",
"fullName": "Alice Smith"
}'
```
The response includes `kycLink` and `tosLink`. Share these with the Entity so they can complete verification.
## 5. Create an Account
Once the Entity exists, create an [Account](/core-objects/account) to hold and move funds.
```bash theme={null}
curl -X POST https://api.otim.com/v0/accounts \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"entityId": "ent_01JA1B2C3D4E5F6G7H8J9K0L",
"name": "Main Account"
}'
```
The Account is automatically provisioned on your default chain. If auto-earn is enabled, idle balances will start earning yield immediately.
## 6. Send a transfer
Move funds from an Account to another Account or to an external [Counterparty](/core-objects/counterparty).
```bash theme={null}
curl -X POST https://api.otim.com/v0/transfers \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"sourceAccountId": "acc_01JA1B2C3D4E5F6G7H8J9K0L",
"destinationAccountId": "acc_02XY9Z8W7V6U5T4S3R2Q1P0N",
"amount": "100.00",
"description": "Payment to vendor"
}'
```
## Next steps
* Set up [Counterparties](/core-objects/counterparty) to send funds to external wallets or bank accounts
* Issue [IBANs](/core-objects/iban) to accept fiat deposits (requires a verified Entity)
* Browse the full [API reference](/api-reference/get-platform-settings)
# Documentation
Source: https://docs.otim.com/docs/introduction
Otim enables any platform to launch their own financial products without partnering with a bank.
Our mission is to supercharge how money earns, moves, and spends in a unified, developer-first way. We took a pragmatic approach to what modern banking infrastructure should look like if it were architected from scratch today. Get access to the bank's business model and brand new revenue mechanics that weren't possible before, built for developers in the simplest form possible.
## Feedback
We're building Otim in the open and your feedback is welcome. Tell us what's working, what's broken, and what you want to see next.
For support or feedback, reach out at [julian@otim.com](mailto:julian@otim.com).
# Action ABI Reference
Source: https://docs.otim.com/docs/resources/appendix/action-hash-abis
This document contains the ABI structures for all Actions, taken from their respective `hash` functions. Use these to properly encode Action arguments when forming EIP-712 hashes.
## DeactivateInstruction
```typescript theme={null}
const deactivateInstructionAbi = [
{ name: 'instructionId', type: 'bytes32' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## Refuel
```typescript theme={null}
const refuelAbi = [
{ name: 'target', type: 'address' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{ name: 'gasLimit', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## RefuelERC20
```typescript theme={null}
const refuelERC20Abi = [
{ name: 'token', type: 'address' },
{ name: 'target', type: 'address' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## Sweep
```typescript theme={null}
const sweepAbi = [
{ name: 'target', type: 'address' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{ name: 'gasLimit', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## SweepCCTP
```typescript theme={null}
const sweepCCTPAbi = [
{ name: 'token', type: 'address' },
{ name: 'destinationDomain', type: 'uint32' },
{ name: 'destinationMintRecipient', type: 'bytes32' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## SweepERC20
```typescript theme={null}
const sweepERC20Abi = [
{ name: 'target', type: 'address' },
{ name: 'token', type: 'address' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## SweepUniswapV3
```typescript theme={null}
const sweepUniswapV3Abi = [
{ name: 'recipient', type: 'address' },
{ name: 'tokenIn', type: 'address' },
{ name: 'tokenOut', type: 'address' },
{ name: 'feeTier', type: 'uint24' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{ name: 'floorAmountOut', type: 'uint256' },
{ name: 'meanPriceLookBack', type: 'uint32' },
{ name: 'maxPriceDeviationBPS', type: 'uint32' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## Transfer
```typescript theme={null}
const transferAbi = [
{ name: 'target', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'gasLimit', type: 'uint256' },
{
name: 'schedule',
type: 'tuple',
components: [
{ name: 'startAt', type: 'uint256' },
{ name: 'startBy', type: 'uint256' },
{ name: 'interval', type: 'uint256' },
{ name: 'timeout', type: 'uint256' }
]
},
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## TransferCCTP
```typescript theme={null}
const transferCCTPAbi = [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'destinationDomain', type: 'uint32' },
{ name: 'destinationMintRecipient', type: 'bytes32' },
{
name: 'schedule',
type: 'tuple',
components: [
{ name: 'startAt', type: 'uint256' },
{ name: 'startBy', type: 'uint256' },
{ name: 'interval', type: 'uint256' },
{ name: 'timeout', type: 'uint256' }
]
},
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## TransferERC20
```typescript theme={null}
const transferERC20Abi = [
{ name: 'token', type: 'address' },
{ name: 'target', type: 'address' },
{ name: 'value', type: 'uint256' },
{
name: 'schedule',
type: 'tuple',
components: [
{ name: 'startAt', type: 'uint256' },
{ name: 'startBy', type: 'uint256' },
{ name: 'interval', type: 'uint256' },
{ name: 'timeout', type: 'uint256' }
]
},
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
## UniswapV3ExactInput
```typescript theme={null}
const uniswapV3ExactInputAbi = [
{ name: 'recipient', type: 'address' },
{ name: 'tokenIn', type: 'address' },
{ name: 'tokenOut', type: 'address' },
{ name: 'feeTier', type: 'uint24' },
{ name: 'amountIn', type: 'uint256' },
{ name: 'floorAmountOut', type: 'uint256' },
{ name: 'meanPriceLookBack', type: 'uint32' },
{ name: 'maxPriceDeviationBPS', type: 'uint32' },
{
name: 'schedule',
type: 'tuple',
components: [
{ name: 'startAt', type: 'uint256' },
{ name: 'startBy', type: 'uint256' },
{ name: 'interval', type: 'uint256' },
{ name: 'timeout', type: 'uint256' }
]
},
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
These ABIs are taken directly from the `hash` function in each Action's contract found on [GitHub](https://github.com/otimlabs/otim-protocol/tree/main/src/actions). Use them to properly encode Action arguments when forming EIP-712 hashes.
See [How to Construct Instruction Activation and Deactivation EIP-712 Hashes](/docs/resources/appendix/instruction-712-hashes) for details on how to use these ABIs.
# API Errors
Source: https://docs.otim.com/docs/resources/appendix/api-errors
All API errors return a JSON body with an `error` message and a machine-readable `code`.
```json theme={null}
{
"error": "Entity not found.",
"code": "not_found"
}
```
## HTTP status codes
| Status | Description |
| ------ | ------------------------------------------------------------------------------------------------------------------------ |
| `400` | Bad request. The request body is invalid or missing required fields. |
| `401` | Unauthorized. The API key is missing or invalid. |
| `403` | Forbidden. The API key does not have permission for this operation. |
| `404` | Not found. The requested resource does not exist. |
| `409` | Conflict. The resource already exists or the idempotency key was already used with different parameters. |
| `422` | Unprocessable. The request was well-formed but could not be completed (e.g., insufficient balance, Entity not verified). |
| `429` | Rate limited. Too many requests. Retry after the duration specified in the `Retry-After` header. |
| `500` | Internal server error. Something went wrong on our end. |
## Common error codes
| Code | Status | Description |
| ---------------------- | ------ | ------------------------------------------------------------------------------------- |
| `unauthorized` | 401 | Missing or invalid API key. |
| `forbidden` | 403 | API key does not have access to this resource. |
| `not_found` | 404 | The requested resource was not found. |
| `validation_error` | 400 | One or more fields in the request body are invalid. |
| `insufficient_balance` | 422 | The source Account does not have enough funds. |
| `entity_not_verified` | 422 | The Entity has not completed KYC/KYB. Required for IBANs and wire transfers. |
| `duplicate_resource` | 409 | A resource with these attributes already exists. |
| `idempotency_conflict` | 409 | The idempotency key was already used with different request parameters. |
| `rate_limited` | 429 | Too many requests. Back off and retry. |
| `transfer_failed` | 422 | The transfer could not be completed. Check the transfer details for more information. |
## Rate limits
The API allows up to 100 requests per second per API key. If you exceed this limit, you will receive a `429` response with a `Retry-After` header indicating how many seconds to wait before retrying.
## Protocol error codes
The following error codes apply to on-chain protocol operations. These are returned when an Instruction execution fails at the contract level.
| Code | Type | Description |
| :--- | :-------- | :---------------------------------------------------------------------- |
| 1201 | Terminal | User account is not delegated to the OtimDelegate contract |
| 1202 | Terminal | Signature verification failed for the provided instruction |
| 1203 | Terminal | Action cannot be executed due to contract-level restrictions |
| 1204 | Retryable | Action execution failed on-chain, may succeed with retry |
| 1205 | Terminal | Instruction has already been deactivated and cannot be executed |
| 1206 | Terminal | Instruction has already been deactivated in DeactivateInstructionAction |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1301 | Terminal | Invalid or malformed arguments provided for transfer action |
| 1302 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| 1303 | Retryable | Insufficient fee token balance to cover execution costs |
| 1304 | Retryable | Insufficient native token balance for the transfer amount |
| Code | Type | Description |
| :--- | :-------- | :---------------------------------------------------------------- |
| 1305 | Terminal | Invalid or malformed arguments provided for ERC20 transfer action |
| 1306 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| 1307 | Retryable | Insufficient fee token balance to cover execution costs |
| 1308 | Retryable | Insufficient ERC20 token balance for the transfer amount |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1309 | Terminal | Invalid or malformed arguments provided for refuel action |
| 1310 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| 1311 | Retryable | Insufficient fee token balance to cover execution costs |
| 1313 | Retryable | Insufficient native token balance for the refuel amount |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1314 | Terminal | Invalid or malformed arguments provided for ERC20 refuel action |
| 1315 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| 1316 | Retryable | Insufficient fee token balance to cover execution costs |
| 1318 | Retryable | Insufficient ERC20 token balance for the refuel amount |
| Code | Type | Description |
| :--- | :-------- | :---------------------------------------------------------------------- |
| 1319 | Terminal | Invalid or malformed arguments provided for UniswapV3 swap action |
| 1320 | Terminal | UniswapV3 pool does not exist for the specified token pair and fee tier |
| 1321 | Terminal | Unexpected invalid tick data encountered during pool validation |
| 1322 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| 1323 | Retryable | Insufficient fee token balance to cover execution costs |
| 1324 | Retryable | Insufficient input token balance for the swap amount |
| 1328 | Retryable | UniswapV3 swap received too little output tokens |
| Code | Type | Description |
| :--- | :-------- | :------------------------------------------------------------------------ |
| 1340 | Retryable | Insufficient fee token balance to cover execution costs |
| 1341 | Terminal | Invalid or malformed arguments provided for deactivate instruction action |
| 1342 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1343 | Terminal | Invalid or malformed arguments provided for sweep CCTP action |
| 1344 | Terminal | CCTP token is not supported for the specified operation |
| 1345 | Retryable | Insufficient fee token balance to cover execution costs |
| 1346 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1347 | Terminal | Invalid or malformed arguments provided for transfer CCTP action |
| 1348 | Retryable | Insufficient token balance for the CCTP transfer amount |
| 1349 | Terminal | CCTP token is not supported for the specified operation |
| 1350 | Retryable | Insufficient fee token balance to cover execution costs |
| 1351 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1352 | Terminal | Invalid or malformed arguments provided for sweep action |
| 1353 | Retryable | Insufficient fee token balance to cover execution costs |
| 1354 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| Code | Type | Description |
| :--- | :-------- | :--------------------------------------------------------------- |
| 1355 | Terminal | Invalid or malformed arguments provided for sweep ERC20 action |
| 1356 | Retryable | Insufficient fee token balance to cover execution costs |
| 1357 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| Code | Type | Description |
| :--- | :-------- | :---------------------------------------------------------------------- |
| 1358 | Terminal | Invalid or malformed arguments provided for sweep UniswapV3 action |
| 1359 | Terminal | UniswapV3 pool does not exist for the specified token pair and fee tier |
| 1360 | Terminal | Unexpected invalid tick data encountered during pool validation |
| 1361 | Terminal | SafeERC20 transfer operation failed due to token contract issues |
| 1362 | Retryable | Insufficient fee token balance to cover execution costs |
# How to Construct Instruction Activation and Deactivation EIP-712 Hashes
Source: https://docs.otim.com/docs/resources/appendix/instruction-712-hashes
This guide shows how to form EIP-712 hashes for instruction activation and deactivation in TypeScript.
As long as you have the Action ABI, you can form the activation hash like this:
```typescript theme={null}
import { keccak256, encodeAbiParameters, encodePacked } from 'viem'
// 1. Define the Instruction ABI (standardized for all Instructions)
const instructionAbi = [
{ name: 'salt', type: 'uint256' },
{ name: 'maxExecutions', type: 'uint256' },
{ name: 'action', type: 'address' },
{ name: 'arguments', type: 'bytes' }
] as const
// 2. Form the domain separator (standardized for all Instructions)
// This matches exactly what's in OtimDelegate.sol
const domainSeparator = keccak256(encodeAbiParameters(
[
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
{ name: 'salt', type: 'bytes32' }
],
[
'OtimDelegate', // OtimDelegate
'1', // Version
chainId, // Current chain ID
delegateAddress, // OtimDelegate contract address
keccak256('ON_TIME_INSTRUCTED_MONEY') // Salt from contract
]
))
// 3. Encode the Action arguments
// Example for `sweepDepositAccountERC20` action
// Get the ABI from action-hash-abis.mdx
const actionAbi = [
{ name: 'token', type: 'address' },
{ name: 'depositor', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'threshold', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
const actionArguments = encodeAbiParameters(
actionAbi,
[
tokenAddress,
depositorAddress,
recipientAddress,
threshold,
{
token: feeTokenAddress,
maxBaseFeePerGas: 0n,
maxPriorityFeePerGas: 1200000n,
executionFee: 0n
}
]
)
// 4. Form the instruction hash
const instructionHash = keccak256(encodeAbiParameters(
instructionAbi,
[
salt, // Random salt
maxExecutions, // Number of times this can be executed
actionAddress, // Address of the action contract
actionArguments // ABI-encoded action arguments
]
))
// 5. Form the final EIP-712 hash
const finalHash = keccak256(encodePacked(
['bytes', 'bytes32', 'bytes32'],
['\x19\x01', domainSeparator, instructionHash]
))
```
For deactivation, it's even simpler since we just need the instruction ID:
```typescript theme={null}
// Form the deactivation hash
const instructionId = "..." // The Instruction ID of the Instruction you want to deactivate
const deactivationHash = keccak256(encodePacked(
['bytes', 'bytes32', 'bytes32'],
[
'\x19\x01',
domainSeparator,
keccak256(encodeAbiParameters(
[{ name: 'instructionId', type: 'bytes32' }],
[instructionId]
))
]
))
```
That's it! The key is having the correct ABI structure, which matches what's defined in the [Instruction.sol contract](https://github.com/otimlabs/otim-protocol/blob/main/src/libraries/Instruction.sol).
In order to swap out the `actionAbi` for the Action you want to activate, look at the `hash` function in the Action's contract ABI. For example, from the RefuelERC20 Action's ABI:
```typescript theme={null}
// From the hash function in RefuelERC20 Action's ABI
// We only need the input type of the hash function
const refuelERC20Abi = [
{ name: 'token', type: 'address' },
{ name: 'target', type: 'address' },
{ name: 'threshold', type: 'uint256' },
{ name: 'endBalance', type: 'uint256' },
{
name: 'fee',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'maxBaseFeePerGas', type: 'uint256' },
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
{ name: 'executionFee', type: 'uint256' }
]
}
] as const
```
This shows us exactly how to structure the arguments for that Action. The rest of the ABI (constructor, other functions, errors) isn't needed for forming the EIP-712 hash.
For a complete reference of all Action ABIs, see [Action Hash ABIs](/docs/resources/appendix/action-hash-abis).
# Changelog
Source: https://docs.otim.com/docs/resources/changelog
See what's new and upcoming with all of Otim's products and platform. ✨
## Entities, platform updates, and vault improvements
* Added entities feature with account linking
* Onchain transfers, onchain balance reading, and balance formatting
* Added Polygon and BNB chains
* Added export private keys functionality for users
* Vault migration support
* Minimum total shares for vaults
* Removed vault underlying token from settlement requests
* Various UI and performance fixes
## Vault support across SDKs
### TS SDK v0.0.18
* Vault migrate support in sdk-core and sdk-server
* Vault minimum total shares
* Underlying token handling for settlement requests
### Go SDK v0.2.0
* Minimum total shares auto detection
* Updated API surface
* Vault migrate support
## Auto-earn, settlement improvements, and orchestration support
### App v0.6.0
* Auto-earn functionality with optimistic UI, historical data, and APY display
* Orchestration settlement with withdraw and settle API integration
* Conditional payment request orchestration form
* Settle and deposit flow
* Rescue funds feature for recovering stuck assets
* Recurring trades and transfers
* Upgraded developer page
### TS SDK v0.0.14
* Settle+deposit and settle+withdraw flows
* New settlement shape and type definitions
* Settlement type and source fields
### Go SDK v0.0.7
* Settlement vault deposit
* Updated orchestration build request struct
* Auto-pull OtimDelegate address from API
## Otim Go SDK initial release
Build and launch Otim orchestrators directly within your Go applications
* EIP-7702 delegation signing
* Settlement building and signing
* Signature parsing and ABI encoding
## Mobile support, virtual accounts, and token upgrades
### New features
* Mobile responsive design
* Virtual accounts
* ERC-4626 vault deposit and withdraw actions
* Token list upgrade and trades revamp
* Multiple passkeys support
* EUR bank connection
### Improvements
* Portfolio chart visualization improvements
* Recurring trade support with proper floor amount handling
* Renamed payment requests to orchestrations
* Session expiration and authentication fixes
## Orchestration support and settlements rename
* Auto build and raw transaction support
* Renamed payment requests to settlements
* Orchestration support with max runs configuration
## TypeScript SDK restructure
* Refactored SDK codebase with new sdk-core and sdk-server packages
* Added activity SDK client
* Switched utils and turnkey packages to production mode
## Passkey authentication, developer tools, and multi-chain
### Authentication
* Passkey-only login with persistent sessions
* Legacy passkey migration from app.otim.com to otim.com
### New features
* Developer tab for API exploration
* QuickBooks invoice search integration
* Multi-chain balances display
* Wallet balance in accounts tab
* Instruction batch signer
* Cancel payment requests
* Chain network type toggle
### Chains
* Added BNB, Unichain, and Polygon mainnet and testnets
## Payment requests, bank connections, and new chains
### Payment requests
* Multi-tenant pay page
* Sign payment requests with passkey
* Payment request form and settlement flow
* QuickBooks integration for payment settings
### Banking
* Bank connection integration
* Link multiple banks
* Bank-to-wallet transfers
### Chains
* Added Base, Arbitrum, and Optimism to the production chain selector
### Other
* Contacts management
* Token balances display
* Various authentication and delegation fixes
## Improved table views, separate mainnet from testnet
* Added chain column to the Overview and Instruction tables
* Separated out testnet chains and made available at [app-testnet.otim.com](https://app-testnet.otim.com)
* Other app performance enhancements and minor bug fixes
## Error and RLP authorization management improvements
* Simple updates around how errors are bubbled up from calls
* Fixed RLP preprocessing for 7702 authlist building
## Otim TypeScript SDK v0.1 release
Build trustless automated executions into your apps and wallets with less than 10 lines of code
* Install simply by running `pnpm add @otim/sdk`, `npm install @otim/sdk`, or `yarn add @otim/sdk`
* Provides simple flows for:
* Setting up the client
* Authenticating users
* Delegating accounts via EIP-7702
* Activating automated instructions
* Compatible with embedded wallet providers like Turnkey, Privy, DFNS, Dynamic, Para, Openfort, etc.
* Seamless integration into existing wallet infrastructure to enable flows like subscriptions, DCA, and topping up wallet balances
## Error handling, seamless authentication
### Dashboard
* Added error message tooltips that show you exactly why an execution didn't work
* Added error toasts for API calls so you'll always know when a request doesn’t go through
* Added pagination to both Activity and Instructions tabs
* Pay for instruction deactivation using any token
### Authentication
* Improved authentication UX to sign in only once and access all supported chains seamlessly
Incorporated several performance improvements to both authentication and dashboard loading
## App improvements, a new fee token
### Instructions
* Added USDT as a fee token option
* Fixed keeping embedded wallet login session live
* Updated default and minimum values for schedule and advanced parameters in the instruction builder
* Several other minor performance and UX flow improvements
### Onboarding
* Added OS-dependent delegation command
* Turned delegation tutorial video into popup
## Ethereum Mainnet deployment
Here are some of the things you can do today on the Otim app:
* Delegate your account to Otim on Ethereum Mainnet
* Create an Otim-delegated account using just your email
* Perform swap, transfer, and refuel actions on a recurring or conditional basis
* Track and manage your instructions in the app dashboard
# Security & Audits
Source: https://docs.otim.com/docs/resources/security-and-audits
## Otim Protocol Security Audits
November 14, 2025
September 11, 2025
May 7, 2025
March 25, 2025
## Bug Bounty Program
soon™️
# Getting Your Developer Keys
Source: https://docs.otim.com/sdk/getting-your-developer-keys
## Step 1: Onboard
Sign up for an Otim account at [app.otim.com](https://app.otim.com).
## Step 2: Request Developer Access
After getting through the onboarding, find your wallet address by clicking the bottom left and notify the Otim team to activate your developer account.
## Step 3: Navigate to the Developer tab
Once the Otim team has notified you that your developer access is ready, you should see the Developer tab appear in your Otim App sidebar.
## Step 4: Create a Developer Key
Firstly, create a Developer Key and make sure to save the returned private key because it won't be shown again.
## Step 5: Create an API Key
Then, create an API Key and make sure to save that too because it will only show once.
***
And that's it! Now you're ready to start orchestrating away with any of our SDKs.
You are allowed to rotate your Developer Key or API Key at any time.
# Create a Basic Settlement Orchestration
Source: https://docs.otim.com/sdk/guides/create-basic-settlement
This guide shows you how to create a settlement orchestration that accepts tokens across multiple chains and settles to USDC on Base.
Make sure you've completed the [Installation & Setup](/sdk/installation-and-setup) before continuing.
## Define the Settlement
A settlement orchestration specifies:
* **Settlement token and chain**: What the recipient receives and where
* **Accepted tokens**: Which tokens on which chains can be used as payment
* **Recipient address**: Who receives the final settlement
```go main.go theme={null}
const (
// Chain IDs
ethereumChainID = 1
baseChainID = 8453
// Token addresses
usdcEth = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
usdcBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
)
// Settlement amount: 1 USDC (6 decimals)
amount := new(big.Int)
amount.SetString("1000000", 10)
// Build settlement request
buildRequest := &client.BuildSettlementOrchestrationRequest{
Params: &client.SettlementParams{
AcceptedTokens: map[client.ChainID][]common.Address{
ethereumChainID: {common.HexToAddress(usdcEth)},
baseChainID: {common.HexToAddress(usdcBase)},
},
SettlementChainId: baseChainID,
SettlementToken: common.HexToAddress(usdcBase),
SettlementAmount: hexutil.Big(*amount),
RecipientAddress: common.HexToAddress(""),
},
}
// Build, sign, and submit
buildResponse, _ := otimClient.BuildSettlementOrchestration(ctx, buildRequest)
newRequest, _ := otimClient.NewOrchestrationFromBuild(ctx, buildResponse)
otimClient.NewOrchestration(ctx, newRequest)
fmt.Println("Request ID:", buildResponse.RequestID)
fmt.Println("Payment Address:", buildResponse.EphemeralWalletAddress.Hex())
```
```typescript index.ts theme={null}
// Define settlement
const settlement = prepareSettlement({
amount: BigInt(1000000), // 1 USDC (6 decimals)
chainId: chains.base.id,
token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
recipient: '',
acceptedTokens: {
[chains.mainnet.id]: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'], // USDC on Ethereum
[chains.base.id]: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'], // USDC on Base
},
maxRuns: 1
});
// Create orchestration
const result = await client.orchestration.create(settlement);
console.log("Request ID:", result.requestId);
console.log("Payment Address:", result.ephemeralWalletAddress);
```
## Understanding the Response
When created successfully, you'll receive:
* **Request ID**: Unique identifier for tracking this orchestration
* **Payment Address**: The ephemeral wallet address to share with whoever is sending payment
The sender transfers their tokens to the payment address, and Otim handles the cross-chain settlement to deliver the specified amount to your recipient.
## Run It
```go Command Line theme={null}
go run main.go
```
```typescript Command Line theme={null}
npx tsx index.ts
```
## Next Steps
* [Track your orchestration](/sdk/guides/track-orchestration) to monitor its status
* Check out the [Otim Playground](https://github.com/otimlabs/otim-playground) for complete working examples
# Track an Orchestration
Source: https://docs.otim.com/sdk/guides/track-orchestration
After creating an orchestration, you can track its status using the request ID returned from the creation response.
## Get Orchestration Details
```go main.go theme={null}
details, err := otimClient.GetOrchestrationDetails(ctx, &client.GetOrchestrationDetailsRequest{
RequestID: requestID,
})
if err != nil {
log.Fatal("Failed to get details:", err)
}
fmt.Printf("Status: %+v\n", details)
```
```typescript index.ts theme={null}
const details = await client.orchestration.getDetails({
requestId: requestId,
});
console.log("Status:", details);
```
## Understanding the Response
The orchestration details include:
* **Status**: Current state of the orchestration (pending, inProgress, paid, completed, failed)
* **Request ID**: The unique identifier for this orchestration
* **Payment Address**: The ephemeral wallet address for receiving payments
* **Settlement Details**: Information about the target chain, token, and amount
## Polling for Updates
For long-running orchestrations, you can poll periodically to check for status changes:
```go main.go theme={null}
for {
details, _ := otimClient.GetOrchestrationDetails(ctx, &client.GetOrchestrationDetailsRequest{
RequestID: requestID,
})
fmt.Println("Current status:", details.Status)
if details.Status == "paid" || details.Status == "completed" || details.Status == "failed" {
break
}
time.Sleep(5 * time.Second)
}
```
```typescript index.ts theme={null}
const pollInterval = 5000; // 5 seconds
const checkStatus = async () => {
const details = await client.orchestration.getDetails({
requestId: requestId,
});
console.log("Current status:", details.status);
if (details.status === "paid" || details.status === "completed" || details.status === "failed") {
return details;
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
return checkStatus();
};
const finalDetails = await checkStatus();
```
## Next Steps
For complete working examples, check out the [Otim Playground](https://github.com/otimlabs/otim-playground).
# Installation & Setup
Source: https://docs.otim.com/sdk/installation-and-setup
Learn how to install and use the Otim server-side SDKs. SDKs are available for [Go](https://github.com/otimlabs/otim-go-sdk) and [TypeScript](https://www.npmjs.com/package/@otim/sdk-server).
## Installation
```go Command Line theme={null}
# Make sure your project is using Go Modules
go mod init
# Install otim-go-sdk
go get github.com/otimlabs/otim-go-sdk
```
```typescript Command Line theme={null}
# Initialize your project
pnpm init
# Install otim-sdk-server
pnpm add @otim/sdk-server
```
## Initialize the Client
```go main.go theme={null}
package main
import (
"context"
"fmt"
"log"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/otimlabs/otim-go-sdk/client"
"github.com/otimlabs/otim-go-sdk/signer"
)
func main() {
ctx := context.Background()
// Initialize the signer with your private key
ethSigner, err := signer.NewEthSigner("")
if err != nil {
log.Fatal("Failed to create signer:", err)
}
// Create the client
otimClient, err := client.NewClient(
ethSigner,
"https://api.otim.com",
"",
)
if err != nil {
log.Fatal("Failed to create client:", err)
}
fmt.Println("Otim client initialized successfully")
}
```
```typescript index.ts theme={null}
import { chains, createOtimServerClient, prepareSettlement } from '@otim/sdk-server';
async function main() {
const client = createOtimServerClient({
appId: "",
privateKey: "",
publicKey: "",
apiKey: "",
environment: "production", // or "sandbox" for testnets
});
await client.init();
console.log('Otim client initialized successfully');
}
main().catch(console.error);
```
# Overview
Source: https://docs.otim.com/sdk/overview
The Otim SDK provides a complete toolkit for integrating Otim’s smart orchestration capabilities directly into your app or workfow.
## Start Building with Otim!
Get developer keys in order to authorized performing actions through the SDK.
### Server-Side
Want to just jump right in with some quickstart sandboxes? Check out the [Otim Playground](https://github.com/otimlabs/otim-playground) GitHub repository to find every developer example you may need.