# Address Book

Keep the addresses your tenant's operations may send to: each active only after a cooling period that starts when it is added, an external one proven by its owner if you ask for it. Every tenant's book records the destinations outside it today; where your tenant enforces it [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md), an operation to an address outside the book is refused before anything is signed.

## Overview

- **An entry** is an address your tenant names: **internal** (one of your own wallets), **external** (someone else's address — a supplier, a customer, an exchange) or **contract** (a contract your operations call — a swap router, a vault). Each has a label, tags, your own reference for the counterparty, and optionally an account whose wallets it serves.
- **No approval step: active after a cooling period.** A new entry — and an entry whose address changes — starts cooling as soon as it is asked for, for your tenant's cooling period (24 hours unless you change it), before an operation may send to it. An external address your settings require to be proven first waits for its proof, which starts its cooling.
- **The book works today for every tenant**: add, prove and change entries and settings at any time. **Every tenant's book is read at its signing points today, in `log`**: a destination outside the book is recorded, and nothing is refused (At the Signing Points). **Refusing those destinations is available on request** [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md). Until then your operations are signed and sent exactly as before, whatever the book holds.
- The routes are under **Address Book** in the API Reference, on the caller's own tenant.

## Who Does What

| What | Roles ([Roles](https://docs.aureahub.com/docs/authentication.md)) |
| --- | --- |
| Read the entries, their history, the settings and their changes | `owner`, `tenantadmin`, `non_signing_admin`, `signer`, `approver`, `editor`, `viewer`, `auditor` — the roles that read the tenant's operations |
| Add, change and remove entries; ask for and send an ownership proof | `owner`, `tenantadmin`, `non_signing_admin`, `editor` |
| Start the cooling of an entry left pending, or reject an entry; change the settings; approve or reject a settings change | `owner`, `tenantadmin`, `non_signing_admin` |

Any other role answers `403` `Tenant admin access required. Your role does not have permission to perform this action.`

## Entries

| kind | You send | What it is |
| --- | --- | --- |
| internal | `walletId` — never an address | One of your tenant's wallets that is not deactivated. Its family, network and address are the wallet's, read from it; its `chain` is `null`. |
| external | `family`, `network`, `address`, and optionally `chain` | Someone else's address, on EVM or Solana. Its owner can prove they hold it (Prove an Address). |
| contract | `network`, `chain`, `address` (`family`, if sent, is `evm`) | An EVM contract. Aurea reads its code on that chain through the platform's own node: no code there answers `422` `ADDRESS_BOOK_ADDRESS_NOT_A_CONTRACT`; a node that cannot answer, `503` `ADDRESS_BOOK_CONTRACT_UNVERIFIED` (`details.retryable`: try again). |

- **`family`** `evm` or `solana`; **`network`** `mainnet` or `testnet`.
- **`chain`**: an EVM chain's slug — `ethereum`, `polygon`, `gnosis`, `bsc`, `arbitrum`, `optimism`, `base`, `avalanche`, `celo`, `flowevm` —: the entry serves that chain only. `null`, left out or `evm`: every EVM chain of the network. Solana is one chain: `solana` or `null`. A contract names its chain.
- **`address`**: on EVM, `0x` and 40 hex digits, kept with its EIP-55 checksum. All lower or all upper case is accepted; a mixed-case address must carry the right checksum (`400` `ADDRESS_BOOK_ADDRESS_CHECKSUM_INVALID`: a typo, most likely). On Solana, base58 of a 32-byte key, kept exactly as written. The zero address and Solana's all-zero key are refused.
- **`label`** (required): 1 to 100 characters, no space at either end. **`tags`**: up to 20, each named once, 1 to 50 of `A–Z a–z 0–9 _ . : -` starting with a letter or a digit. **`customerRefId`**: your own reference for the counterparty, 1 to 128 of `A–Z a–z 0–9 _ . : @ / + -` starting with a letter or a digit.
- **`accountId`**: one of your tenant's [accounts](https://docs.aureahub.com/docs/guide-accounts.md). The entry then serves only that account's wallets; without it, every wallet of your tenant.
- **One live entry per address** — per account, family, network, chain and address, among the entries pending, cooling or active: `409` `ADDRESS_BOOK_ENTRY_EXISTS`. A rejected or removed address may be added again as a new entry.

## The Life of an Entry

```text
added ──► cooling ──its cooling period ends──► active
  │  └──(a cooling period of 0)────────────────► active
  └──(external, a proof required)──► pending_approval ──proven──► cooling
pending_approval ──reject──► rejected (final)
cooling ──reject or remove──► removed (final)        pending_approval, active ──remove──► removed (final)
pending_approval, cooling, active ──its address changes──► cooling again (next version), or pending_approval for its proof
```

- **Only an active entry is a destination.** There is no way to `active` but the end of its cooling period.
- **A label, tags or `customerRefId` change at once**, whatever the state ([Change an Entry](https://docs.aureahub.com/docs/address-book-entry-change.md)).
- **A new address** — `address`, `network` or `chain`; an internal entry's `walletId` — is the entry's next `version`, unproven: it cools again from that request, or, an external address your settings require to be proven, waits in `pending_approval` for its proof. Its `kind`, `family` and `accountId` never change: add another entry.
- **Rejected and removed are final**: the entry never changes again (`409` `ADDRESS_BOOK_ENTRY_FINAL`). Removal is immediate from any live state and soft — the entry and its history stay readable.
- **The history** of an entry ([Get an Entry](https://docs.aureahub.com/docs/address-book-entry-get.md)) keeps every move, oldest first, with each version's address and who made it — `scope` `tenant` for your users, `job` for the activation.

## No Approval Step

- **An entry is not approved: it cools from its request.** [Add an Entry](https://docs.aureahub.com/docs/address-book-entry-add.md) answers it `cooling` until `activeAt` — or `active` at once when your cooling period is 0 —, and so does a new address ([Change an Entry](https://docs.aureahub.com/docs/address-book-entry-change.md)). Its `approval` then says that nobody approved it: `approvedBy` `null`, `approvedScope` the scope of who asked, `approvedAt` the moment its cooling started, `requiredApprovals` `0`.
- **A proof first, where your settings require one**: an external entry waits in `pending_approval` until its owner proves it ([Prove Ownership](https://docs.aureahub.com/docs/address-book-proof-verify.md)); the proof starts its cooling.
- **[Approve an Entry](https://docs.aureahub.com/docs/address-book-entry-approve.md)** is left for an entry still in `pending_approval` — one asked for while entries were approved, or an external address waiting for its proof, which is proven first (`409` `ADDRESS_BOOK_PROOF_REQUIRED`): it starts its cooling, by any of your administrators, who asked included. A cooling or active entry answers `409` `ADDRESS_BOOK_ENTRY_NOT_PENDING` with `details.approvalStep` `none`: nothing is left to approve.
- **[Reject an Entry](https://docs.aureahub.com/docs/address-book-entry-reject.md)**: a pending entry is `rejected`; a cooling one is withdrawn before it is active — `removed`, exactly as [Remove an Entry](https://docs.aureahub.com/docs/address-book-entry-remove.md) does it; an active one answers `409` `ADDRESS_BOOK_ENTRY_NOT_PENDING` (remove it). Who asked may reject too, to take back a request.
- Who asked is whoever added the entry or last changed its address.
- A change of the settings that lowers a protection still waits for its approval (Settings).

## Cooling Period

- **24 hours by default** (`86400` seconds), from 0 to 30 days (`2592000`) in your settings. A tenant on the institutional security profile cools at least 24 hours: a shorter setting is refused (`400` `ADDRESS_BOOK_COOLING_TOO_SHORT`), and a cooling period never starts shorter.
- **When its cooling starts** — at its request, or at its proof —, the entry keeps the period it starts (`coolingPeriodSeconds`) and `activeAt` = `approval.approvedAt` + that period. A later change of the settings never moves a cooling period already started. `effectiveCoolingPeriodSeconds` in the settings is the period an entry added now would start.
- **Active at `activeAt`.** From then the entry reads `active` and is a destination, whether or not its activation was written yet; while it cools, `coolingSecondsLeft` counts down.

## Prove an Address

An external entry's owner can prove they hold its key: they sign a message Aurea writes for that entry. Only an external entry is proven (`409` `ADDRESS_BOOK_PROOF_NOT_EXTERNAL`): an internal one is your own wallet, a contract has no key.

1. [Ask for a challenge](https://docs.aureahub.com/docs/address-book-proof-challenge.md): `201` with `challengeId` and the `message` to sign. Valid 10 minutes, used once; at most 5 open per entry (`409` `ADDRESS_BOOK_CHALLENGE_LIMIT`).
2. The address's owner signs `message` exactly as given: on EVM with `personal_sign` (EIP-191) — the signature as `0x` and 130 hex digits; on Solana an ed25519 signature of the message's UTF-8 bytes, in base58. Only a key signs: an EVM smart-contract account cannot prove an address.
3. [Send the signature](https://docs.aureahub.com/docs/address-book-proof-verify.md) with the `challengeId`: the entry is proven — `proof.proven` `true`, its `method` (`eip191` or `solana_ed25519`) and `provenAt`. Its status and version don't change.

```text
Aurea asks you to prove that you control this address, to receive funds from an Aurea address book.
Signing this message moves no funds and costs nothing.

Address: 0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
Address type: EVM
Network: mainnet
Tenant: 6f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0
Entry: 8f3c2a71-5d4e-4b9a-a6c1-2e7f9d0b3c58 (version 1)
Nonce: 9b1f6c0d2e4a8c3b7f5e1d9a0c2b4e6f8a1c3e5d7b9f0a2c4e6b8d0f1a3c5e7b
Issued at: 2026-10-09T08:05:00.000Z
Expires at: 2026-10-09T08:15:00.000Z
```

- The message names your tenant's id, the entry and its version: the person who signs sees them.
- **Five tries.** A signature that cannot be read (`400` `ADDRESS_BOOK_SIGNATURE_MALFORMED`) or is not the address's over this challenge (`400` `ADDRESS_BOOK_SIGNATURE_WRONG_SIGNER`) leaves `details.attemptsLeft`; the fifth burns the challenge (`409` `ADDRESS_BOOK_CHALLENGE_BURNT`) — ask for a new one.
- Past its 10 minutes: `410` `ADDRESS_BOOK_CHALLENGE_EXPIRED`. Used already: `409` `ADDRESS_BOOK_CHALLENGE_USED`. Issued before the entry's address changed: `409` `ADDRESS_BOOK_CHALLENGE_STALE`.
- A new address clears the proof: prove the new one.

## Settings

| Setting | Default | What it does |
| --- | --- | --- |
| coolingPeriodSeconds | `86400` | The cooling period an entry starts: 0 to 30 days (at least 24 hours on the institutional profile). |
| proofRequired | `false` | `true`: an external entry is proven before its cooling starts. |
| oneTimeAddresses | `false` | `true`: an operation may also go to an address outside the book — recorded as a one-time address, never refused for it. |

- **A change that raises or keeps the protections applies at once**: [Change the Settings](https://docs.aureahub.com/docs/address-book-settings-put.md) answers `200`, `state` `applied`.
- **A change that lowers one** — a shorter cooling period, the proof no longer required, one-time addresses switched on — **waits for its approval**: `202`, `state` `pending`. [Approve a Change](https://docs.aureahub.com/docs/address-book-change-approve.md) applies it, by an administrator other than who asked where your tenant's quorum applies — the institutional profile, or two administrators or more (active users with the role `owner`, `tenantadmin` or `non_signing_admin`): `403` `ADDRESS_BOOK_QUORUM_REQUESTER` for who asked; [Reject a Change](https://docs.aureahub.com/docs/address-book-change-reject.md) refuses it — or withdraws it, when who asked rejects it. So one-time addresses are switched on only through an approved change.
- **One change waits at a time**: another answers `409` `ADDRESS_BOOK_SETTINGS_PENDING` with `details.changeId`. Every change asked for is listed in [Setting Changes](https://docs.aureahub.com/docs/address-book-changes-list.md).

## At the Signing Points

What the book does to your operations is set by Aurea, tenant by tenant, and shown in your settings' `mode`:

| mode | What your operations meet |
| --- | --- |
| off | Nothing is read: every operation is signed and sent as before, whatever the book holds. |
| log | **Every tenant's book today. Recorded, never refused.** Each operation's destination is judged against the book when it is about to be signed; one outside the book is recorded and told to your endpoints that name `address_book.destination.unlisted`. Every answer is the one it would be without the book. A tenant on the institutional security profile is in `log` by its profile. |
| enforce | **On request. Refused before anything is signed.** Your tenant moves from `log` to `enforce` (`enforced` `true`) once its book holds its destinations. A destination outside the book answers `403` `ADDRESS_BOOK_DESTINATION_NOT_ALLOWED` — nothing signed, handed out to sign or sent. |

A destination is allowed when it is, on the operation's family, network and chain:

- **an active entry** — one serving every wallet, or the account of the wallet that signs — whose `chain` is that chain or `null`;
- **one of your tenant's own wallets**, even without an entry;
- **any address, with one-time addresses on**: recorded as `one_time`, never refused.

- **Where it applies**: wherever Aurea is about to sign, hand out to sign or send a transaction that has a destination — sends and their broadcasts, token approvals and swaps (custodial and client-side), dApp broadcasts, and the other routes that sign a transaction. A contract call's destination is the contract — a swap's router, a vault, a pool —: list the contracts you use as `contract` entries before `enforce`. A message to sign has no destination: the book does not judge it.
- **Your policy's destination kind `address_book`.** Where the book is read — every tenant's, today —, a destination that is an active entry is of the kind `address_book` as well as its own: a listed contract is both `contract` and `address_book`, a listed one-time address both `one_time` and `address_book`. A [policy set](https://docs.aureahub.com/docs/guide-policy-sets.md)'s rule naming `address_book` matches it, so a policy can say "transfers only to the address book" — enforced where your policy is (the institutional profile), whatever the book's own mode. Your own wallets stay `internal` unless an internal entry lists them. A book that could not be read leaves the kind unknown: a rule allowing `address_book` then does not match, and a restrictive one counts as a possible match.
- **The refusals** carry the signing point in `details.point`, and are answered as they are by every money route:

| Status | details.code | message |
| --- | --- | --- |
| 403 | ADDRESS_BOOK_DESTINATION_NOT_ALLOWED | `The destination is not an active entry of the tenant's address book, and one-time addresses are off` |
| 403 | ADDRESS_BOOK_DESTINATION_UNRESOLVED | `The address book cannot tell this destination's chain or network, and the tenant only sends to its address book` |
| 503 | ADDRESS_BOOK_UNAVAILABLE | `The address book could not be read: nothing was signed` — with `details.retryable` `true`: try again. |

```json
{
  "statusCode": 403,
  "error": "ForbiddenError",
  "message": "The destination is not an active entry of the tenant's address book, and one-time addresses are off",
  "details": { "code": "ADDRESS_BOOK_DESTINATION_NOT_ALLOWED", "point": "custodial_transfer" }
}
```

[Contact us](https://www.aureahub.com/contact) to have your tenant's book refuse the destinations outside it.

## Events and Audit

- **Events**, only to the [webhook endpoints](https://docs.aureahub.com/docs/guide-events.md) that name them: `address_book.entry.requested`, `.approved`, `.activated`, `.rejected`, `.removed` and `.proven` with the entry, and `address_book.destination.unlisted` — [Event Types](https://docs.aureahub.com/docs/guide-events.md). An entry added sends `address_book.entry.requested` as it starts cooling, and `address_book.entry.activated` when the cooling ends; `.approved` is sent only for an entry left pending that an administrator approves. A testnet entry's events go to your sandbox endpoints.
- **Your [audit trail](https://docs.aureahub.com/docs/audit-events.md)** [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md), records every move — `address_book.entry.requested`, `address_changed`, `changed`, `approved`, `activated`, `rejected`, `removed`, `proven`; `address_book.settings.requested`, `changed`, `rejected` (category `governance`); `address_book.enforcement.changed` when refusing is switched on or off for your tenant (category `security`) — never an address, a label, a tag or your reference.

## Refusals

The usual envelope, the code in `details.code`:

| Status | details.code | message |
| --- | --- | --- |
| 400 | ADDRESS_BOOK_REQUEST_INVALID | `Invalid request`, each problem in `details.errors` (e.g. `walletId: an internal entry names one of the tenant's wallets`, `(body): nothing to change`, an unknown field or query parameter, an id that is not a UUID); or, on a change, `An internal entry's address is its wallet's: name another walletId`, `Only an internal entry names a wallet`, `An entry keeps its chain family: add another entry for another family` |
| 400 | ADDRESS_BOOK_ADDRESS_CHECKSUM_INVALID | `The address has a wrong EIP-55 checksum: check it for a typo, or send it in lower case` |
| 400 | ADDRESS_BOOK_ADDRESS_INVALID | `The address is not one of its family`, or `The zero address cannot be an entry: nobody holds its key` |
| 400 | ADDRESS_BOOK_CHAIN_UNKNOWN | `The address book does not know the chain … for …`, `A contract entry names the EVM chain its code is on`, or `The address book keeps EVM and Solana addresses` (an internal entry's wallet on another chain) |
| 400 | ADDRESS_BOOK_WALLET_NOT_FOUND | `The tenant holds no such wallet` |
| 400 | ADDRESS_BOOK_ACCOUNT_NOT_FOUND | `The tenant holds no such account` |
| 400 | ADDRESS_BOOK_COOLING_TOO_SHORT | `An institutional tenant's cooling period is at least 24 hours` |
| 400 | ADDRESS_BOOK_SIGNATURE_MALFORMED | `The signature cannot be read`, with `details.attemptsLeft` |
| 400 | ADDRESS_BOOK_SIGNATURE_WRONG_SIGNER | `The signature is not the address's over this challenge`, with `details.attemptsLeft` |
| 403 | ADDRESS_BOOK_QUORUM_REQUESTER | `The tenant's quorum applies: an administrator other than who asked approves this change` — a settings change |
| 404 | ADDRESS_BOOK_ENTRY_NOT_FOUND | `Address book entry not found` — also another tenant's entry |
| 404 | ADDRESS_BOOK_CHALLENGE_NOT_FOUND | `This entry has no such challenge` |
| 404 | ADDRESS_BOOK_SETTINGS_CHANGE_NOT_FOUND | `Settings change not found` |
| 409 | ADDRESS_BOOK_ENTRY_EXISTS | `The book already holds a live entry for this address, chain and account` |
| 409 | ADDRESS_BOOK_ENTRY_FINAL | `A rejected entry never changes again` (or `removed`), with `details.status` |
| 409 | ADDRESS_BOOK_ENTRY_NOT_PENDING | Approving a cooling or active entry: `There is no approval step: an entry cools from its request, nothing is left to approve`; rejecting an active one: `Only an entry pending or cooling is rejected: an active entry is removed` — with `details.status` and `details.approvalStep` `none` |
| 409 | ADDRESS_BOOK_ENTRY_CHANGED | `The entry changed while this request was read: send it again` |
| 409 | ADDRESS_BOOK_PROOF_REQUIRED | `The tenant requires an external address to be proven before it is approved` |
| 409 | ADDRESS_BOOK_PROOF_NOT_EXTERNAL | `Only an external address is proven: an internal one is the tenant's own wallet, a contract has no key` |
| 409 | ADDRESS_BOOK_CHALLENGE_LIMIT | `An entry has at most 5 open challenges: sign one or let it expire first` |
| 409 | ADDRESS_BOOK_CHALLENGE_USED | `This challenge was already used` |
| 409 | ADDRESS_BOOK_CHALLENGE_STALE | `The entry's address changed after this challenge was issued: ask for a new one` |
| 409 | ADDRESS_BOOK_CHALLENGE_BURNT | `Too many wrong signatures (5): this challenge is burnt, ask for a new one` |
| 409 | ADDRESS_BOOK_SETTINGS_PENDING | `A change of the settings already waits for its approval: approve, reject or withdraw it first`, with `details.changeId` |
| 409 | ADDRESS_BOOK_SETTINGS_CHANGE_NOT_PENDING | `Only a change waiting for its approval is decided`, with `details.status` |
| 410 | ADDRESS_BOOK_CHALLENGE_EXPIRED | `This challenge has expired: ask for a new one` |
| 422 | ADDRESS_BOOK_ADDRESS_NOT_A_CONTRACT | `No contract exists at this address on chain 8453`, with `details.chainId` |
| 503 | ADDRESS_BOOK_CONTRACT_UNVERIFIED | `The contract cannot be checked: the node for chain 8453 did not answer` (or `no node is configured for chain …`), with `details.chainId` and `details.retryable` `true` |

- The refusals that name a field carry it in `details.field` (`address`, `chain`, `walletId`, `accountId`, `coolingPeriodSeconds`).
- At the signing points: `ADDRESS_BOOK_DESTINATION_NOT_ALLOWED`, `ADDRESS_BOOK_DESTINATION_UNRESOLVED` and `ADDRESS_BOOK_UNAVAILABLE` (At the Signing Points).
- A role that may not call a route answers `403`; a frozen user or tenant `423` ([Freezes & Quarantines](https://docs.aureahub.com/docs/guide-freezes.md)).

---

Web version: https://docs.aureahub.com/#guide-address-book
