# Account Structures

Declare, per network, how your customers' coins are held: segregated — one account per customer, nothing moves by itself — or omnibus, where each customer's collection account is swept to your treasury, withdrawals are paid from a pool of your accounts, and a sub-ledger keeps what each customer is owed.

## Overview

- **One structure per network**, `mainnet` and `testnet` each on its own:

  - `segregated` — **every tenant's today, and until it declares otherwise**: each customer's coins stay in the wallets of the customer's own account, and nothing moves by itself. A tenant that declares nothing sees no change.
  - `omnibus` — each customer has a **collection account**; what reaches its wallet is swept to your **treasury** under your **sweep policies**; your customers' withdrawals are paid from a **pool** of your accounts; and a **sub-ledger** keeps each customer's **claim** — what the omnibus owes them — reconciled with the chain.
- **The members are accounts you created** ([Accounts](https://docs.aureahub.com/docs/guide-accounts.md)) — never a user's default account —, each in one role on a network. The omnibus works with each member account's EVM wallet on that network: one address, valid on every EVM chain ([Accounts](https://docs.aureahub.com/docs/guide-accounts.md) › One EVM Key per Account).
- **EVM chains only.** The omnibus's assets are those with a sweep policy, on the EVM chains the Hub supports (Sweep Policies, below). Solana is not served.
- **Amounts** are whole numbers of the asset's smallest unit, as decimal text ([Amount Units](https://docs.aureahub.com/docs/amounts.md)). **An asset** is `native` — the chain's coin — or the token's contract address, answered in lower case.
- **The routes** are under **Account Structures** in the API Reference — [List](https://docs.aureahub.com/docs/structures-list.md), [Get](https://docs.aureahub.com/docs/structures-get.md) and [Declare](https://docs.aureahub.com/docs/structures-put.md) a structure; [List](https://docs.aureahub.com/docs/structures-collection-list.md), [Add](https://docs.aureahub.com/docs/structures-collection-add.md) and [Remove](https://docs.aureahub.com/docs/structures-collection-remove.md) collection accounts; [List](https://docs.aureahub.com/docs/structures-policies-list.md), [Set](https://docs.aureahub.com/docs/structures-policy-put.md) and [Remove](https://docs.aureahub.com/docs/structures-policy-delete.md) sweep policies; the [sub-ledger](https://docs.aureahub.com/docs/structures-ledger.md), its [entries](https://docs.aureahub.com/docs/structures-ledger-entries.md), the [reconciliation](https://docs.aureahub.com/docs/structures-reconciliation.md), the [transfers](https://docs.aureahub.com/docs/structures-transfers-list.md); and [Request a Pool Withdrawal](https://docs.aureahub.com/docs/structures-withdrawal-create.md) —, on the caller's own tenant, with an access token (Who Calls the Routes, below).

```json
{
  "network": "mainnet",
  "structure": "omnibus",
  "poolRule": "priority",
  "fundingAddresses": ["0x661edf1a2bd8d4826b91cc9089e1044ccd556bad"],
  "treasuryAccountId": "3e3f3321-a179-423c-b4fb-8a4fe6bc5756",
  "pool": [
    { "accountId": "a9dcb8c6-9bee-4900-b792-cd01bd77820b", "position": 0, "isTreasury": false },
    { "accountId": "3e3f3321-a179-423c-b4fb-8a4fe6bc5756", "position": 1, "isTreasury": true }
  ],
  "collectionAccounts": 3,
  "sweepPolicies": [
    {
      "chainId": 8453,
      "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "symbol": "USDC",
      "decimals": 6,
      "minAmount": "50000000",
      "leaveBehind": "0",
      "intervalSeconds": 3600,
      "windowFromMinute": 1320,
      "windowToMinute": 360,
      "enabled": true,
      "updatedAt": "2026-10-13T09:20:11.402Z"
    },
    {
      "chainId": 8453,
      "asset": "native",
      "symbol": "ETH",
      "decimals": 18,
      "minAmount": "10000000000000000",
      "leaveBehind": "200000000000000",
      "intervalSeconds": 86400,
      "windowFromMinute": null,
      "windowToMinute": null,
      "enabled": true,
      "updatedAt": "2026-10-13T09:21:47.090Z"
    }
  ],
  "jobOn": false,
  "updatedBy": "454bda73-ba10-4bac-9e62-e28e4e575007",
  "updatedAt": "2026-10-13T09:12:40.511Z"
}
```

On Base, an omnibus with three collection accounts: their USDC is swept to the treasury between 22:00 and 06:00 UTC once a wallet holds 50 USDC, at most once an hour per wallet; their ETH once a day above 0.01 ETH, leaving 0.0002 ETH for the next token sweeps' fees. Withdrawals are paid by the first pool account that can, then the treasury.

## Today and Coming

> Note: **Declaring works today; the omnibus's processing is coming** ([Feature Availability](https://docs.aureahub.com/docs/feature-availability.md)). Until then nothing is opened, posted, swept, paid out or reconciled, and a structure's `jobOn` is `false`.

- **Every tenant, today**: declare a structure, add and remove collection accounts, set and remove sweep policies, and read everything.
- **Once the processing runs** — `jobOn` `true` —, Aurea opens the sub-ledger, posts what reaches the member wallets, sweeps under your policies, sends your pool withdrawals and reconciles each asset, pass after pass.

| Route | What it answers until then |
| --- | --- |
| List, Get, Declare a Structure | Your declaration, with `jobOn` `false`. |
| Collection accounts and sweep policies | Added, listed and removed as described. No claim exists yet, so nothing blocks a removal. |
| Read the Sub-Ledger | `{ "claims": [], "house": [] }` |
| List Sub-Ledger Entries | `{ "entries": [], "nextCursor": null }` |
| Read the Reconciliation | `{ "assets": [] }` |
| List Transfers | `{ "transfers": [], "nextCursor": null }`; Get a Transfer, `404` `STRUCTURE_NOT_FOUND`. |
| Request a Pool Withdrawal | `503` `OMNIBUS_JOB_OFF` (`Pool withdrawals are switched off on this platform`) to a request written right: nothing is debited or queued. |

Coins already in a collection wallet when Aurea first opens it count as its customer's claim (The Sub-Ledger, below).

## The Omnibus's Accounts

| Role | What it is |
| --- | --- |
| collection | A customer's account. What reaches its wallet from outside the omnibus is the customer's — a deposit to their claim — and is swept to the treasury. Added with [Add Collection Accounts](https://docs.aureahub.com/docs/structures-collection-add.md), up to 1,000 a call. |
| treasury | One per network: where every sweep goes. It pays withdrawals only when it is also in the pool. |
| pool | The accounts that pay withdrawals, in order — up to 50; the treasury may be one of them. |

- **One role per account and network.** A collection account cannot be the treasury or in the pool, and the other way round: `409` `STRUCTURE_ACCOUNT_ROLE`, with `details.accountId`.
- **Funding addresses** (`fundingAddresses`, up to 20): your own EVM addresses — the wallet you pay gas from, your top-ups. What they send to a collection wallet is **the house's** — your tenant's own money in the omnibus —, never the customer's.
- **The pool's rule** (`poolRule`) picks the pool account that pays a withdrawal, among those that can pay it now:

| poolRule | Pays the withdrawal |
| --- | --- |
| priority | The first in pool order. The default. |
| largest_balance | The one holding the most of the asset; pool order between equals. |

A pool account can pay when the platform holds its wallet's key for your tenant, its wallet is opened for the asset, no transfer of that wallet is on its way on the chain, and it holds the amount and the fee — for a token, the token for the amount and the chain's native coin for the fee; for the native coin, the two together.

## Declare an Omnibus

```bash
curl -X PUT https://api.aureahub.com/v1/structures/mainnet \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "structure": "omnibus",
    "treasuryAccountId": "3e3f3321-a179-423c-b4fb-8a4fe6bc5756",
    "poolAccountIds": ["a9dcb8c6-9bee-4900-b792-cd01bd77820b", "3e3f3321-a179-423c-b4fb-8a4fe6bc5756"],
    "poolRule": "priority",
    "fundingAddresses": ["0x661edf1a2bd8d4826b91cc9089e1044ccd556bad"]
  }'
```

- **A treasury the first time** (`treasuryAccountId`): `400` `STRUCTURE_TREASURY_REQUIRED` otherwise. Every account named must be one you created: `400` `STRUCTURE_ACCOUNT_UNKNOWN`, the unknown ones in `details.accountIds`.
- **A field left out keeps what the network has**: the treasury, the pool, the rule (`priority` the first time), the funding addresses. `poolAccountIds` is the whole pool, in order: the list you send replaces it.
- **A replaced treasury stays**, as a pool account — at the end of the pool unless you list it elsewhere: its coins stay counted, and withdrawals can drain it. Nothing leaves the omnibus silently.
- **A pool account already in the sub-ledger stays in the pool**: leaving it out answers `409` `STRUCTURE_ACCOUNT_OPENED`.
- **Back to `segregated`**: send `{ "structure": "segregated" }` alone — a treasury, a pool, a rule or funding addresses with it answer `400` `STRUCTURE_REQUEST_INVALID` (`A segregated structure has no treasury, pool or funding addresses`). Only with no customer's claim and no transfer on its way: `409` `STRUCTURE_LEDGER_NOT_EMPTY` otherwise, with `details.claims` (whether a claim is held) and `details.openTransfers`. The network's members and sweep policies are then removed; its entries and transfers stay, as history.
- Collection accounts and sweep policies are set on an omnibus network only: `409` `STRUCTURE_NOT_OMNIBUS` on a segregated one.
- **Leaving the omnibus**: a collection account is removed only with no claim of its customer (`409` `STRUCTURE_ACCOUNT_HOLDS_CLAIMS`) and no transfer of it on its way (`409` `STRUCTURE_ACCOUNT_BUSY`).

## Sweep Policies

- **One per chain and asset of the network**, set with [Set a Sweep Policy](https://docs.aureahub.com/docs/structures-policy-put.md) at `…/sweep-policies/{chainId}/{asset}`. Setting it again replaces it whole: a field left out takes its default.
- **An asset with a policy is an omnibus asset**, enabled or not: its sub-ledger is kept and it is reconciled. A withdrawal is asked for in an omnibus asset only.
- **`chainId`**: an EVM chain of the network (the table below) — `400` `STRUCTURE_CHAIN_UNSUPPORTED` for any other, a testnet's id on `mainnet` included.
- **`asset`**: `native`, or the token's contract address in any case (kept in lower case). It must be in your tenant's list of the chain's tokens — the one [Tokens by Chain](https://docs.aureahub.com/docs/tokens-by-chain.md) answers: `400` `STRUCTURE_ASSET_UNKNOWN` otherwise. The policy's `symbol` and `decimals` are read from that list. A token of your own is added with [Register an Asset](https://docs.aureahub.com/docs/asset-register.md) — on a chain where your tenant lists none yet, that makes the chain's token lists your own ([Asset & Chain Registry](https://docs.aureahub.com/docs/guide-asset-registry.md) › Your Token Lists).

| Field | Meaning and rule |
| --- | --- |
| minAmount | The least a sweep moves. Above 0. Required. |
| leaveBehind | What a sweep leaves in the collection wallet. Default `"0"`. For the native coin, leave the fees of the wallet's next token sweeps. |
| intervalSeconds | The least time between two sweeps of one wallet and asset — made, failed or skipped: 60 to 2,592,000 (30 days). Required. |
| window | `{ "fromMinute", "toMinute" }`, minutes of the day in UTC (0–1439): sweeps run from `fromMinute` up to, not including, `toMinute`; `fromMinute` above `toMinute` wraps past midnight; the two equal is refused. Left out or `null`: any time. Answered as `windowFromMinute` and `windowToMinute`. |
| enabled | Default `true`. `false`: not swept; the asset's sub-ledger is still kept. |

- **Removing a policy** ([Remove a Sweep Policy](https://docs.aureahub.com/docs/structures-policy-delete.md)): only with no customer's claim on the asset and no transfer of it on its way — for `native`, no transfer at all on the chain —: `409` `STRUCTURE_LEDGER_NOT_EMPTY` otherwise.

| Chain | chainId on mainnet | chainId on testnet |
| --- | --- | --- |
| Ethereum | 1 | 11155111 |
| Polygon | 137 | 80002 |
| Gnosis | 100 | 10200 |
| BNB Smart Chain | 56 | 97 |
| Arbitrum | 42161 | 421614 |
| Optimism | 10 | 11155420 |
| Base | 8453 | 84532 |
| Avalanche | 43114 | 43113 |
| Celo | 42220 | 11142220 |
| Flow EVM | 747 | 545 |

## Sweeps

Once the omnibus processing runs, each pass takes every enabled policy whose window is open, and every collection wallet opened for its asset — once the treasury's wallet is opened for it too:

1. **Due?** The wallet's last sweep of the asset — made, failed or skipped — is at least `intervalSeconds` old.
2. **How much**: the wallet's balance of the asset, less `leaveBehind`, less — for the native coin — the fee the sweep itself pays. Below `minAmount`: nothing happens, and nothing is recorded.
3. **Who signs**: the collection wallet's key, which Aurea must hold — a wallet whose key is on the user's device or in an MPC wallet is `skipped`, `WALLET_NOT_CUSTODIAL`. A token sweep pays its fee in the chain's native coin: a wallet without enough is `skipped`, `SWEEP_NO_GAS`, until it holds enough — send it the native coin from one of your funding addresses — never counted as the customer's —, and give a native policy a `leaveBehind` that keeps it.
4. **One at a time**: a wallet with a transfer `planned`, `signed` or `sent` on the chain gets no other until it ends.

- A sweep goes to the treasury's wallet. Coins moving between the omnibus's own wallets change no claim.
- Each sweep — made, failed or skipped — is a transfer you read with [List Transfers](https://docs.aureahub.com/docs/structures-transfers-list.md). A skipped sweep is recorded again each time it falls due, until the wallet can be swept.
- Every read and send goes through Aurea's own node of the chain.

## The Sub-Ledger

- Per chain and omnibus asset: **each customer's claim** — what the omnibus owes them, never negative — and **the house**: your tenant's own position, what you put in less the fees the omnibus's sends paid. [Read the Sub-Ledger](https://docs.aureahub.com/docs/structures-ledger.md) answers both; [List Sub-Ledger Entries](https://docs.aureahub.com/docs/structures-ledger-entries.md), every entry, newest first.
- **The opening.** Aurea opens each member wallet for each omnibus asset at the latest block it has read for incoming transfers on that chain: what the wallet holds then is posted — a collection wallet's to its customer, a treasury or pool wallet's to the house. A member or a policy added later is opened at a later pass.
- **After the opening**, each incoming transfer of an omnibus asset to an opened wallet is posted: to a collection wallet from outside the omnibus, its customer's deposit — unless it comes from one of your funding addresses: the house's —; to the treasury or a pool wallet, the house's; from another wallet of the omnibus, nothing.
- **A re-organisation** of the chain that removes a posted transfer posts its reversal, and a transfer that comes back is posted again. A customer's claim never goes negative: a reversal that would make it so is not posted, and the reconciliation shows the difference.
- **A collection wallet sends only through the omnibus.** Every route that would send from a collection account's EVM wallet refuses it, `403` `OMNIBUS_COLLECTION_WALLET`, nothing signed or sent: a transfer ([Create Transaction](https://docs.aureahub.com/docs/tx-create.md)), a swap, an Earn or lending operation, a bank ramp payout, a dApp's transaction or message, and a client-side or MPC wallet's prepared transaction, its broadcast or its signing ceremony. It holds from the moment the account is a collection account of that network's omnibus — the wallet on that network, mainnet or testnet — until it leaves the collection. A freeze or a quarantine of the wallet answers first (`423`). **Its key is not exported either** while the account is in the collection — [Request Export Token](https://docs.aureahub.com/docs/wallets-export-token.md) and [Export Encrypted Key](https://docs.aureahub.com/docs/wallets-export-key.md) answer the same `403`, on mainnet and testnet alike —: an exported key could send the customer's coins outside the omnibus. Signatures that send nothing and hand no key out — an address proof for the bank ramp, a sign-in message, an MPC backup proof — go on. Pay a customer out with a pool withdrawal.
- **A send you make yourself from the treasury or a pool wallet** ([Create Transaction](https://docs.aureahub.com/docs/tx-create.md)) is not refused and posts nothing: coins that leave the omnibus that way show as a difference in the reconciliation.

| kind | Whose | Posted when |
| --- | --- | --- |
| opening | customer | The collection wallet's balance when it was opened. |
| deposit | customer | A transfer reached the collection wallet from outside the omnibus, not from a funding address. |
| deposit_reversal | customer | A re-organisation removed that transfer (negative). |
| withdrawal | customer | A pool withdrawal was requested: debited at once (negative). |
| withdrawal_reversal | customer | That withdrawal failed: its amount back. |
| house_opening | house | A treasury or pool wallet's balance when it was opened. |
| house_deposit | house | A transfer reached the treasury or a pool wallet, or a collection wallet from a funding address. |
| house_deposit_reversal | house | A re-organisation removed that transfer (negative). |
| fee | house | The network fee a sweep or a withdrawal paid, in the chain's native coin (negative) — when the native coin is an omnibus asset of that chain. |
| adjustment | house | Aurea closed a shortfall the reconciliation found — coins that left the omnibus with no entry — on the house's side (negative): your tenant's own position bears it, and no customer's claim changes. Nothing moves on chain. |

An entry names what posted it: `sourceKind` `opening`, `inbound_transfer` (an incoming transfer), `omnibus_transfer` (a sweep or a withdrawal: `sourceId` is the transfer's id) or `adjustment` (Aurea's adjustment, by its id). Entries are never changed or removed; a correction is a new entry.

## Reconciliation

Each pass, per omnibus asset: the sum of every member wallet's balance at the block read against the customers' claims plus the house. [Read the Reconciliation](https://docs.aureahub.com/docs/structures-reconciliation.md) answers the last verdict of each asset.

| status | statusCode | Meaning |
| --- | --- | --- |
| matched | `null` | The chain holds exactly the claims plus the house, and the house is not negative. |
| mismatch | `DIFFERENCE` | The chain holds more or less: `difference` is `onChain` − `customers` − `house`. `mismatchSince`: since when. A pass that finds them equal again ends it. |
| mismatch | `HOUSE_NEGATIVE` | Equal, but the house is below zero: the omnibus's fees were paid with customers' coins. |
| pending | `IN_FLIGHT` | A transfer of the asset — for the native coin, any transfer on the chain — is on its way, or ended after the block read: the figures are shown, not judged. |
| unread | `INBOUND_OFF` | Aurea does not yet follow incoming transfers on this chain: nothing is opened or reconciled there. |
| unread | `NO_PLATFORM_NODE` | Aurea has no node of this chain. |
| unread | `NOT_OPENED` | A member wallet is not opened for the asset yet. |
| unread | `CHAIN_UNREAD` | A balance could not be read this pass. |

An `unread` verdict has no figures: `onChain`, `customers`, `house` and `difference` are `null`.

**A shortfall** — a `DIFFERENCE` below zero: the member wallets hold less than the claims plus the house — is closed by Aurea with `adjustment` entries on the house's side (The Sub-Ledger, above), never beyond the shortfall. Once they cover it, the next pass finds the asset `matched` — or `HOUSE_NEGATIVE` if they took the house below zero, until you fund it: send the missing coins to the treasury or a pool wallet.

## Pool Withdrawals

- **Ask for one** with [Request a Pool Withdrawal](https://docs.aureahub.com/docs/structures-withdrawal-create.md): the customer's collection account (`accountId`), `chainId`, an omnibus asset of that chain (`asset`), `amount`, `destination` and your own id for it (`externalId`).
- **Debited at once**: the amount leaves the customer's claim (a `withdrawal` entry) — never more than the claim: `409` `OMNIBUS_INSUFFICIENT_CLAIM`, with the claim in `details.claim` — and the withdrawal is queued: `202`, `status` `queued`.
- **Paid from the pool.** At the next pass Aurea sends it from the pool account the rule picks, through your signing rules (Signing Rules and Operations, below), and follows it to its receipt. While no pool account can pay it, it waits, `reason` `NO_POOL_WALLET`. Sweeps fill the treasury, not the pool: put the treasury in the pool, or keep your pool accounts funded.
- **A withdrawal that fails gives its amount back** to the claim (a `withdrawal_reversal` entry).
- **Idempotent on `externalId`** — 1 to 128 of `A–Z a–z 0–9 _ . : -`, unique among your tenant's withdrawals on both networks. The same request again — the same network and body — answers `200` with the same withdrawal, as it is now; anything else under the same id, `409` `OMNIBUS_EXTERNAL_ID_TAKEN` with the withdrawal's id in `details.transferId`. The route takes no `Idempotency-Key`: `externalId` is the key.
- **Refused before anything is debited**: the zero address (`400` `OMNIBUS_DESTINATION_INVALID`), a wallet of the omnibus as the destination (`400` `OMNIBUS_DESTINATION_INTERNAL`), an account that is not a collection account of the network (`400` `OMNIBUS_ACCOUNT_NOT_COLLECTION`), an asset with no sweep policy on that chain (`400` `OMNIBUS_ASSET_UNKNOWN`), a segregated network (`409` `STRUCTURE_NOT_OMNIBUS`).
- **Until the omnibus processing runs**: `503` `OMNIBUS_JOB_OFF`.

## A Transfer's Statuses

Each sweep and each pool withdrawal is a transfer: `kind` `sweep` or `withdrawal`.

```text
withdrawal:  queued ──► planned ──► signed ──► sent ──► confirmed
sweep:                  planned ──► signed ──► sent ──► confirmed
                           └──────────┴──────────┴──► failed      (reason)
skipped: a sweep that fell due and was not made           (reason)
```

| status | Meaning |
| --- | --- |
| queued | A withdrawal waiting for a pool account: `reason` `null`, `NO_POOL_WALLET` while none can pay it, or `INTERRUPTED` when it was put back after a send that never left. |
| planned | Its wallet picked — `walletId`, `fromAddress`, and for a withdrawal the pool account in `sourceAccountId` —, about to be signed. |
| signed | Signed: its `nonce` and `txHash` are kept before the node is handed it. After a restart Aurea reads the chain: nothing is sent twice. |
| sent | The node took it (`sentAt`). |
| confirmed | Mined and successful (`blockNumber`, `finishedAt`), with the `fee` it paid. |
| failed | Ended without moving its amount; `reason` says why (below). A withdrawal's amount is back in the claim. |
| skipped | A sweep that fell due and was not made: `WALLET_NOT_CUSTODIAL` or `SWEEP_NO_GAS`. |
| reason, when failed | Meaning |
| REVERTED | Mined, and reverted: the fee was paid, nothing moved. |
| DROPPED | Sent, never mined: another transaction of the wallet took its nonce. |
| SUPERSEDED | Signed, never reached the node, and its nonce was taken. |
| INTERRUPTED | A sweep that never reached the node — stopped before or after it was signed, its nonce still free. A withdrawal stopped so goes back to `queued`. |
| A refusal's code | Refused before anything was signed — for example `ACCOUNT_FROZEN`, `TENANT_FROZEN`, `ADDRESS_BOOK_DESTINATION_NOT_ALLOWED`, or your institutional tenant's policy's code. |

- **`fee`** is in the smallest unit of the chain's native coin, whatever the asset moved; `null` until the transaction is mined, and for one never mined.
- `accountId` is the customer — a sweep's collection account, a withdrawal's customer —; `toAccountId`, a sweep's treasury; `externalId`, a withdrawal's id of yours.

## Signing Rules and Operations

- **Every send passes your tenant's signing rules** before Aurea signs it, as any send of a key the platform holds for your tenant: a sweep with the collection wallet's key, a withdrawal with the pool account's wallet's key. A frozen tenant, user, account or wallet ([Freezes & Quarantines](https://docs.aureahub.com/docs/guide-freezes.md)); the [address book](https://docs.aureahub.com/docs/guide-address-book.md), where your tenant enforces it — the treasury is one of your own wallets, a withdrawal's destination is judged as any other —; your institutional tenant's policy ([Policy Sets](https://docs.aureahub.com/docs/guide-policy-sets.md)) and the other [signing rules](https://docs.aureahub.com/docs/guide-signing-rules.md) enforced for your tenant.
- **A refusal ends the transfer `failed`**, with the refusal's code as `reason`. An omnibus send is never held for an approval: a rule that would hold it ends it the same way. A withdrawal's amount goes back to the claim.
- Each send is made under its wallet's nonce lock: a sweep and a send of your own from the same wallet never take the same nonce.
- **Each send signed is an operation of your tenant** ([Operations & Fees](https://docs.aureahub.com/docs/guide-operations.md)), recorded whether or not your other operations are:

| type | The operation |
| --- | --- |
| SWEEP | A sweep: `origin` `omnibus.sweep`, no `userId` (Aurea starts it), `walletId` the collection wallet, `destination` the treasury's address. |
| TRANSFER | A pool withdrawal: `origin` `omnibus.withdrawal`, `userId` the user who asked, `walletId` the pool account's wallet, `destination` the withdrawal's. |

- Recorded at `SIGNED` `signed_by_hub`, then `BROADCASTING` `sending` and `CONFIRMING` `awaiting_confirmation`, and ended `COMPLETED` `confirmed` or `FAILED` — `reverted`, `dropped` or `submission_failed` —, with the transfer's `reason` as `failureReason`. `providerRef` is `omnibus_transfer:` and the transfer's id; the transfer's `operationId` names the operation. A send refused before it was signed has none.
- Read them with [List Operations](https://docs.aureahub.com/docs/operations-list.md) (`type=SWEEP`) and [Get an Operation](https://docs.aureahub.com/docs/operation-get.md), with the roles that read your tenant's operations. Account structures have no event type of their own: the operations' `operation.*` events are theirs ([Webhooks & Events](https://docs.aureahub.com/docs/guide-events.md)).

## Audit Trail

Your [audit trail](https://docs.aureahub.com/docs/audit-events.md) [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md), records each change — ids, networks, chain ids, assets, amounts and counts, never an address:

| action | category | When |
| --- | --- | --- |
| structure.changed | governance | A structure declared (`before`, `after`, `treasuryChanged`, `poolAccounts`). |
| structure.members_added | governance | Collection accounts added (`added`, `already`) — when at least one was new. |
| structure.member_removed | governance | A collection account removed. |
| structure.sweep_policy_set | governance | A sweep policy set. |
| structure.sweep_policy_removed | governance | A sweep policy removed. |
| omnibus.withdrawal_requested | money | A pool withdrawal requested: the claim debited, the payout queued. |
| omnibus.adjustment_recorded | money | Aurea closed a shortfall with an `adjustment` entry on the house's side (network, chain id, asset, amount and the shortfall). |

The sends themselves are in the trail as their operations' `operation.submitted` and `operation.state_changed` (category `money`).

## Who Calls the Routes

- **With an access token** (`Authorization: Bearer`), on `/v1/structures…`, for the caller's own tenant: your server calls them signed in as one of your users with the role. The tenant signature and server tokens are not accepted here.

| What | Roles ([Roles](https://docs.aureahub.com/docs/authentication.md)) |
| --- | --- |
| Read: structures, collection accounts, policies, the sub-ledger and its entries, the reconciliation, the transfers | `owner`, `tenantadmin`, `non_signing_admin`, `signer`, `approver`, `editor`, `viewer`, `auditor` — the roles that read your tenant's operations |
| Declare a structure; add and remove collection accounts; set and remove sweep policies | `owner`, `tenantadmin`, `non_signing_admin` |
| Request a pool withdrawal | `owner`, `tenantadmin`, `signer` — your signers: never the `non_signing_admin` |

- Any other role: `403` `Tenant admin access required. Your role does not have permission to perform this action.`
- **A request written wrong is refused whole**, never repaired: `400` `STRUCTURE_REQUEST_INVALID`, with each problem in `details.errors` (up to 50) as `field: reason`. A body field, or a query parameter, the route does not take is refused too.

```json
{
  "statusCode": 400,
  "error": "BadRequestError",
  "message": "Invalid request",
  "details": {
    "code": "STRUCTURE_REQUEST_INVALID",
    "errors": [
      "structure: Invalid enum value. Expected 'segregated' | 'omnibus', received 'pooled'",
      "poolAccountIds.0: Invalid uuid",
      "(body): Unrecognized key(s) in object: 'treasury'"
    ]
  }
}
```

## Refusals

The usual envelope, the code in `details.code`. Nothing is written.

| Status | details.code | message |
| --- | --- | --- |
| 400 | STRUCTURE_REQUEST_INVALID | `Invalid request`, with `details.errors` — a `cursor` the route did not give included (`cursor: not one this API gave`) —; or `A segregated structure has no treasury, pool or funding addresses` |
| 400 | STRUCTURE_ACCOUNT_UNKNOWN | `Each member is one of the tenant's own accounts (not a user's default account)`, the unknown ones in `details.accountIds` (up to 20) |
| 400 | STRUCTURE_TREASURY_REQUIRED | `An omnibus structure names its treasury account` |
| 400 | STRUCTURE_CHAIN_UNSUPPORTED | `Chain 84532 is no EVM chain of the mainnet the Hub supports`, with `details.chainId` |
| 400 | STRUCTURE_ASSET_UNKNOWN | `The asset is not in the tenant's list of the chain: register it first (the asset registry or the token routes)`, with `details.chainId` and `details.asset` |
| 400 | OMNIBUS_DESTINATION_INVALID | `The destination is the zero address` |
| 400 | OMNIBUS_ACCOUNT_NOT_COLLECTION | `The account is no collection account of the omnibus` |
| 400 | OMNIBUS_ASSET_UNKNOWN | `The asset is not an omnibus asset of this chain (it has no sweep policy)` |
| 400 | OMNIBUS_DESTINATION_INTERNAL | `The destination is one of the omnibus's own wallets` |
| 403 | OMNIBUS_COLLECTION_WALLET | `This wallet collects deposits for the tenant's omnibus structure: its coins leave only through the omnibus (its sweeps to the treasury, and withdrawals paid from the pool). A direct send from it is refused.` — at any route that would send from a collection wallet; at a key export, `This wallet collects deposits for the tenant's omnibus structure: its key is not exported while its account is in the collection (its coins leave only through the omnibus).` |
| 404 | STRUCTURE_NOT_FOUND | `No such collection account here.`, `No such sweep policy here.` or `No such transfer here.` — also another tenant's, or another network's |
| 409 | STRUCTURE_NOT_OMNIBUS | `The tenant's structure on this network is not omnibus`, with `details.network` |
| 409 | STRUCTURE_ACCOUNT_ROLE | `A collection account cannot be the treasury or in the pool` (declaring), or `The treasury and pool accounts cannot be collection accounts` (adding collection accounts), with `details.accountId` |
| 409 | STRUCTURE_ACCOUNT_OPENED | `This pool account holds part of the omnibus's coins in the sub-ledger: it stays in the pool`, with `details.accountId` |
| 409 | STRUCTURE_LEDGER_NOT_EMPTY | `The omnibus still holds customers' claims or transfers on their way` (back to segregated; `details.claims`, `details.openTransfers`), or `Customers still hold claims on this asset, or a transfer of it is on its way` (removing a policy) |
| 409 | STRUCTURE_ACCOUNT_HOLDS_CLAIMS | `The customer still holds a claim on the omnibus` |
| 409 | STRUCTURE_ACCOUNT_BUSY | `A transfer of this account is on its way` |
| 409 | OMNIBUS_INSUFFICIENT_CLAIM | `The customer's claim on the omnibus is smaller than the amount`, with `details.claim` |
| 409 | OMNIBUS_EXTERNAL_ID_TAKEN | `Another withdrawal has this externalId`, with `details.transferId` — absent when two requests raced for the id |
| 503 | OMNIBUS_JOB_OFF | `Pool withdrawals are switched off on this platform` — until the omnibus processing runs |

---

Web version: https://docs.aureahub.com/#guide-account-structures
