> ## Documentation Index
> Fetch the complete documentation index at: https://dev.enterprise.moonpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Crypto Addresses

> Register wallet addresses for Travel Rule compliance before using them in any Autoramp flow.

Every wallet address that interacts with MoonPay Enterprise must be registered (linked) to a customer **before** it can be used in any Autoramp flow: [onramp](/onramp), [offramp](/offramp), or [stable to stable](/stable-to-stable). This is a regulatory requirement under the **[Travel Rule](/travel-rule)**, which obliges Virtual Asset Service Providers to exchange originator and beneficiary information for crypto transfers.

MoonPay Enterprise supports two wallet types:

* **Self-hosted wallets**: the customer controls the private key. Register with a signed proof-of-ownership message. For customers in the **US** and **Rest of World** who cannot sign from their custody UI (e.g. Fireblocks), register with a self-attested declaration instead, with no signature.
* **Hosted wallets**: the wallet is custodied by another VASP (e.g. Coinbase, Kraken). Registration requires the VASP's DID so MoonPay Enterprise can exchange travel-rule data with the custodian.

<Warning>
  Autoramp creation will fail if the recipient wallet address has not been registered first.
</Warning>

<Note>
  If the blockchain is Stellar, the wallet must already have an established USDC trustline. This applies to all three registration endpoints below. In production, registering a Stellar address without one returns a `400` with the message: "Stellar account {address} does not have a USDC trustline established. Please add a USDC trustline before registering this address." The sandbox skips this check.
</Note>

## Verify a self-hosted crypto address

Submit a signature proving ownership of your cryptocurrency wallet. The verification process requires signing a specific message with your wallet's private key.

<Steps>
  <Step title="Format the proof message:">
    ```bash theme={null}
    I am verifying ownership of the wallet address {wallet_address} as customer {customer_id}. This message was signed on {date} to confirm my control over this wallet.
    ```

    Replace:

    * `{customer_id}` with your customer ID
    * `{wallet_address}` with your wallet address
    * `{date}` with today's date in DD/MM/YYYY format using UTC timezone

    Example:

    ```bash theme={null}
    I am verifying ownership of the wallet address 0x742d35Cc6634C0532925a3b844Bc454e4438f44e as customer 4b85d15e-f343-41c0-809c-85314cae2fa6. This message was signed on 23/05/2025 to confirm my control over this wallet.
    ```
  </Step>

  <Step title="Sign this message using your wallet's private key" />

  <Step title="Submit the proof:">
    <CodeGroup>
      ```bash Bash theme={null}
      curl -X POST https://api.sandbox.iron.xyz/api/addresses/crypto/selfhosted \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: 123e4567-e89b-12d3-a456-426614174000" \
      -H "X-API-Key: $API_KEY" \
      -d '{
        "customer_id": "4b85d15e-f343-41c0-809c-85314cae2fa6",
        "address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
        "message": "I am verifying ownership of the wallet address 0x742d35Cc6634C0532925a3b844Bc454e4438f44e as customer 4b85d15e-f343-41c0-809c-85314cae2fa6. This message was signed on 23/05/2025 to confirm my control over this wallet.",
        "signature": "0xa5f1751b75a28c12694f02590d29b8cdd68b4f5c783273a75823fc6cfeaa702f1a65fa7c1838ae799fe755c92443cb51631d000922329cdd2ce799eee75f42531b",
        "blockchain": "Ethereum"
      }'
      ```
    </CodeGroup>
  </Step>
</Steps>

<Note>
  The date must be in DD/MM/YYYY format and within **one day** of today's UTC date. Yesterday, today, and tomorrow are all accepted, which absorbs clock skew and local timezones that run ahead of or behind UTC. Anything outside that window fails, so don't carry a stale date over from a template.
</Note>

<Note>
  `Idempotency-Key` is required on this `POST`, so a replayed signature registers the address only once. MoonPay Enterprise stores successful responses only, so a retry after an error runs the registration again. See [Idempotency](/idempotency).
</Note>

<Note>
  The message must be **byte-identical** between sign-time and POST-time (no rewording, no trailing newline, no whitespace edits), and the `customer_id` inside the message must equal the `customer_id` in the request body. EVM signatures use standard EIP-191 `personal_sign` (the default for `cast wallet sign`, ethers `signMessage`, viem `signMessage`); smart-contract wallets verify via EIP-1271. Not-yet-deployed (counterfactual) smart-contract wallets sign per ERC-6492, which wraps the EIP-1271 signature so it can be verified before the wallet's first on-chain deployment. You can add additional text before or after the required message and verification still succeeds.
</Note>

### Error response

A `400` is returned when the proof message or the signature does not validate. These address endpoints return the error as a plain string body, not a structured object:

<CodeGroup>
  ```json theme={null}
  "Invalid signature! Cannot register address"
  ```
</CodeGroup>

Common causes:

* The proof message does not match the required format
* The date in the proof message is more than one day from today's UTC date
* The signature does not verify against the submitted address
* The `blockchain` does not match the address format
* The wallet address is already registered, which returns `409`
* The customer ID does not belong to your partner, which returns `401`

## Register a self-attested crypto address

For direct customers who cannot produce a signature from their custody UI (e.g. Fireblocks), register a self-hosted wallet by declaring ownership instead of signing a proof-of-ownership message. The wallet is still registered as self-hosted and carries the same [Travel Rule](/travel-rule) obligations as the signed flow.

<Info>
  Self-attestation is available only to customers in the **US** and **Rest of World** jurisdictions. Every other customer, including those in the UK, EEA, Canada, and Australia, must use the signed [self-hosted flow](#verify-a-self-hosted-crypto-address) and gets a `403` from this endpoint. A customer whose region MoonPay Enterprise cannot resolve also gets a `403`. The sandbox skips this region check, so the example below works for any sandbox customer.
</Info>

<CodeGroup>
  ```bash Bash theme={null}
  curl -X POST https://api.sandbox.iron.xyz/api/addresses/crypto/attested \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 123e4567-e89b-12d3-a456-426614174002" \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "customer_id": "4b85d15e-f343-41c0-809c-85314cae2fa6",
    "address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
    "blockchain": "Ethereum",
    "name": "Fireblocks vault"
  }'
  ```
</CodeGroup>

The registration requires:

* `customer_id`: The customer's UUID
* `address`: The wallet address the customer declares they control
* `blockchain`: The blockchain the address is on (e.g. `Ethereum`, `Solana`, `Polygon`, `Base`, `Arbitrum`)
* `name`: Optional display name for this wallet

<Note>
  The `Idempotency-Key` header is required so the address is only registered once on retries. Pass the optional `X-Sub-Partner-Id` header to register the wallet under a specific sub-partner.
</Note>

A successful registration returns the verified address. Attested wallets come back with `address_type: "SelfHosted"` and the sentinel `proof_signature: "SELF_ATTESTED"`, which marks them as declared rather than cryptographically signed:

<CodeGroup>
  ```json theme={null}
  {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "wallet_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
    "address_type": "SelfHosted",
    "blockchain": "Ethereum",
    "created_at": "2025-06-19T10:30:00+00:00",
    "disabled": false,
    "is_self": true,
    "name": "Fireblocks vault",
    "proof_message": "Self-attested ownership (no signature provided)",
    "proof_signature": "SELF_ATTESTED",
    "vasp_did": null
  }
  ```
</CodeGroup>

### Error response

A `409` is returned when the wallet address is already registered. These address endpoints return the error as a plain string body, not a structured object:

<CodeGroup>
  ```json theme={null}
  "A crypto address with this wallet address already exists"
  ```
</CodeGroup>

## Register a hosted wallet

When the recipient wallet is custodied by another institution (VASP), for example Coinbase, Kraken, or Binance, you need to register the wallet address along with the institution's DID so MoonPay Enterprise can exchange travel-rule data with the custodian.

<Steps>
  <Step title="Search for the VASP (wallet provider):">
    First, find the DID (Decentralized Identifier) of the institution that hosts the wallet:

    <CodeGroup>
      ```bash Bash theme={null}
      curl -X GET "https://api.sandbox.iron.xyz/api/addresses/crypto/hosted/vasps?q=bitstamp&limit=10" \
      -H "X-API-Key: $API_KEY"
      ```
    </CodeGroup>

    Response:

    ```json theme={null}
    [
      {
        "did": "did:ethr:0x367f004f1062f68c038ac8d3c3071fedc18fb689",
        "name": "Bitstamp",
        "country": "LU",
        "website": "https://www.bitstamp.net/"
      }
    ]
    ```
  </Step>

  <Step title="Register the hosted wallet address:">
    Submit the wallet address with the VASP's DID:

    <CodeGroup>
      ```bash Bash theme={null}
      curl -X POST https://api.sandbox.iron.xyz/api/addresses/crypto/hosted \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: 123e4567-e89b-12d3-a456-426614174001" \
      -H "X-API-Key: $API_KEY" \
      -d '{
        "customer_id": "4b85d15e-f343-41c0-809c-85314cae2fa6",
        "wallet_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
        "vasp_did": "did:ethr:0x367f004f1062f68c038ac8d3c3071fedc18fb689",
        "blockchain": "Ethereum"
      }'
      ```
    </CodeGroup>
  </Step>
</Steps>

The registration requires:

* `customer_id`: The customer's UUID
* `wallet_address`: The wallet address at the institution
* `vasp_did`: The DID (Decentralized Identifier) of the institution hosting the wallet (obtained from the VASP search)
* `blockchain`: The blockchain the address is on (e.g. `Ethereum`, `Solana`, `Polygon`, `Base`, `Arbitrum`)

You can also pass an optional `name` to set a display name for the wallet.

<Note>
  The wallet must be registered in your name at the hosting institution. MoonPay Enterprise will use the VASP DID to exchange travel rule information with the institution.
</Note>

<Note>
  `Idempotency-Key` is required on this `POST`, so the address is registered only once on retries. MoonPay Enterprise stores successful responses only, so a retry after an error runs the registration again. See [Idempotency](/idempotency).
</Note>

## List verified addresses

Retrieve all verified cryptocurrency addresses for a customer:

<CodeGroup>
  ```bash Bash theme={null}
  curl -X GET https://api.sandbox.iron.xyz/api/addresses/crypto/{customer_id} \
  -H "X-API-Key: $API_KEY"
  ```
</CodeGroup>

Optionally filter by address type using the `filter` query parameter:

* `?filter=Hosted`: Only hosted wallet addresses
* `?filter=SelfHosted`: Only self-hosted wallet addresses
* `?filter=All` or no filter: All addresses (default)

Example with filter:

<CodeGroup>
  ```bash Bash theme={null}
  curl -X GET "https://api.sandbox.iron.xyz/api/addresses/crypto/4b85d15e-f343-41c0-809c-85314cae2fa6?filter=SelfHosted" \
  -H "X-API-Key: $API_KEY"
  ```
</CodeGroup>

The response includes details about each verified address:

* `id`: Address ID
* `wallet_address`: The wallet address
* `address_type`: "Hosted" or "SelfHosted"
* `blockchain`: The blockchain (e.g. "Ethereum", "Solana")
* `created_at`: Registration timestamp
* `disabled`: Whether the address is disabled
* `is_self`: Whether the wallet belongs to the customer themselves or to a third party
* `name`: Display name, if one was set at registration
* `vasp_did`: (For hosted wallets only) The VASP's DID
* `proof_message`: (For self-hosted wallets only) The verification message
* `proof_signature`: (For self-hosted wallets only) The signature

<Note>
  Self-attested wallets are returned with `address_type: "SelfHosted"`, so `?filter=SelfHosted` includes them. To tell them apart from cryptographically signed wallets, check `proof_signature`: attested wallets carry the sentinel value `"SELF_ATTESTED"` and a `proof_message` that marks the wallet as self-attested rather than signed.
</Note>

## Disable or enable an address

You can disable or enable a verified crypto address. Disabled addresses cannot be used for transactions.

<CodeGroup>
  ```bash Bash theme={null}
  curl -X PUT https://api.sandbox.iron.xyz/api/addresses/crypto/{address_id}/disabled \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "customer_id": "4b85d15e-f343-41c0-809c-85314cae2fa6",
    "disabled": true
  }'
  ```
</CodeGroup>

<Note>
  Addresses that were automatically disabled due to high risk (e.g., flagged by Chainalysis) cannot be re-enabled.
</Note>

## Related guides

<CardGroup>
  <Card title="Fiat Sources Verification" horizontal icon="shield" href="/fiat-addresses" />

  <Card title="Travel Rule Compliance" horizontal icon="plane" href="/travel-rule" />
</CardGroup>
