# Ask for an Address Challenge

Get the message a user signs to prove they control an address outside Aurea — the first step of the standalone mode.

## Overview

With the **standalone** mode your users bring their own wallet: a pay-in can be delivered to an address outside Aurea once the user has proved they control it ([Standalone Pay-In](https://docs.aureahub.com/docs/guide-standalone-payin.md)). The proof is a signature. This endpoint issues the message; the user signs it with the address's key; [Verify an Address](https://docs.aureahub.com/docs/bank-address-verify.md) checks the signature and keeps the address.

- **The standalone mode must be switched on** for your tenant in that environment (`modes.standalone` in [Tenant Settings](https://docs.aureahub.com/docs/bank-settings.md)), otherwise `403` with `details.code` `NOAH_MODE_OFF`.
- `isTestnet: true` proves the address for the sandbox; `false` or omitted, for production. A proof counts only in its own environment.
- `family` is `evm` or `solana`. One EVM proof covers the address on every EVM network.
- The response carries the address in the one form Aurea stores and compares: an EVM address with its checksum, a Solana address in base58. A mixed-case EVM address must have a valid checksum (all lowercase is fine). An address that is not one of its kind, or the EVM zero address, answers `400` `NOAH_ADDRESS_INVALID`.
- `message` names the address, its kind, the environment, your tenant, the user, a random nonce and the expiry. **Sign it exactly as returned**, line breaks included:

  - **EVM**: an EIP-191 `personal_sign` from the account that owns the address — for example `signer.signMessage(message)` in ethers. A smart contract account cannot prove an address this way.
  - **Solana**: an ed25519 signature of the message's UTF-8 bytes, written in base58 — for example a wallet adapter's `signMessage`.

  Signing moves no funds and costs no fee.
- A challenge can be verified for 10 minutes (`expiresAt`). A user holds at most 5 open challenges — ones neither used, burnt nor expired — across addresses and environments; beyond that `409` `NOAH_ADDRESS_CHALLENGE_LIMIT`.
- Every call issues a new challenge, also for an address the user already proved.
- Answers `403` to a token without a tenant, and when your tenant does not use the bank ramp.

## Endpoint

### `POST /v1/ramp/bank/addresses/challenge`

Authentication: bearer token required.

Issues the message that proves one address of the user in one environment.

**Request body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `family` | string | yes | `evm` or `solana` |
| `address` | string | yes | The address to prove, up to 100 characters |
| `isTestnet` | boolean | no | `true` for the sandbox; `false` or omitted for production |

**Responses**

`201` Created

```json
{
  "challengeId": "5b1d3f7e-2c4a-4e6b-9d8f-0a1b2c3d4e5f",
  "family": "evm",
  "address": "0xC0207704CaEB9342cf491Ad4a177F43592df65a7",
  "environment": "sandbox",
  "message": "Aurea asks you to prove that you control this address, to use it with the bank ramp.\nSigning this message moves no funds and costs nothing.\n\nAddress: 0xC0207704CaEB9342cf491Ad4a177F43592df65a7\nAddress type: EVM\nEnvironment: sandbox\nTenant: 3f0e1c2a-8b7d-4c6e-9a5f-1d2c3b4a5e6f\nUser: 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d\nNonce: 8f3c2a9d4e1b7f6a0c5d2e8b3a9f1c7d4e6b0a2f8c3d5e9a1b7c4f0d6e2a8b3c\nIssued at: 2026-09-16T10:00:00.000Z\nExpires at: 2026-09-16T10:10:00.000Z",
  "issuedAt": "2026-09-16T10:00:00.000Z",
  "expiresAt": "2026-09-16T10:10:00.000Z"
}
```

`403` Standalone off

```json
{
  "statusCode": 403,
  "error": "ForbiddenError",
  "message": "The bank ramp's standalone mode is not switched on for this tenant in sandbox.",
  "details": { "code": "NOAH_MODE_OFF", "mode": "standalone", "environment": "sandbox" }
}
```

`400` Not an address

```json
{
  "statusCode": 400,
  "error": "BadRequestError",
  "message": "0xc0207704caeb9342cf491ad4a177f43592df65A7 is not a valid EVM address: its checksum is wrong",
  "details": { "code": "NOAH_ADDRESS_INVALID" }
}
```

`409` Too many open

```json
{
  "statusCode": 409,
  "error": "ConflictError",
  "message": "A user has at most 5 open challenges: sign one or let it expire first",
  "details": { "code": "NOAH_ADDRESS_CHALLENGE_LIMIT", "limit": 5 }
}
```

## Implementation

```javascript
// EVM, in the browser with ethers v6: prove the connected account for the sandbox
import { BrowserProvider } from 'ethers';

async function signAddressChallenge(token) {
  const signer = await new BrowserProvider(window.ethereum).getSigner();

  const res = await fetch('https://api.aureahub.com/v1/ramp/bank/addresses/challenge', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
    body: JSON.stringify({ family: 'evm', address: await signer.getAddress(), isTestnet: true })
  });
  const challenge = await res.json();
  if (!res.ok) throw new Error(`${res.status}: ${challenge.message}`);

  // EIP-191 personal_sign of the message exactly as returned
  const signature = await signer.signMessage(challenge.message);
  return { challengeId: challenge.challengeId, signature }; // send both to /verify
}
```

---

Web version: https://docs.aureahub.com/#bank-address-challenge
