> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentcard.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# iMessage

> Give an iMessage agent a wallet by texting a link. Tapping it opens the wallet, nothing to install.

This is the wallet for agents that live in iMessage. There's nothing to embed and no Messages extension. Your agent texts the user their wallet link, and tapping it opens their wallet.

## Create a wallet link (server side)

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.agentcard.sh/api/v2/wallet_links \
    -H "Authorization: Bearer $ORG_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"user_id": "USER_ID"}'
  ```

  ```javascript Node theme={null}
  const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${ORG_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ user_id: "USER_ID" }),
  });
  const link = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
      "https://api.agentcard.sh/api/v2/wallet_links",
      headers={"Authorization": f"Bearer {ORG_TOKEN}"},
      json={"user_id": "USER_ID"},
  )
  link = res.json()
  ```
</CodeGroup>

The response includes a `url` like `https://app.agentcard.sh/w/...`. That URL is the whole integration.

## Text the link

Send the URL in the conversation, worded however your agent talks:

> Here's your wallet, add a card and I can start buying for you:
> [https://app.agentcard.sh/w/](https://app.agentcard.sh/w/)...

Links are safe to text. Messaging apps that unfurl previews don't consume the link, because the preview fetch never exchanges it.

## What the user sees

The link opens the hosted wallet page: they add a card or approve a payment, then they're back in the conversation. It works on every device, and there's nothing to install.

One link survives multiple taps. Each open exchanges it for a short session, and a link allows up to 20 opens inside its lifetime, 15 minutes by default (`expires_in` accepts 60 to 86400 seconds). For a returning user, have your agent create a fresh link at the moment it's needed rather than reusing old ones.

A native App Clip version of the wallet ships with our iOS app: the same links will open it directly on iPhone, and nothing about your integration changes when it does.

## Webhooks you will receive

* `wallet_link.opened` the first time the user opens the link
* `connected_card.updated` when they add a card
* `transaction.*` events when payments happen

Your agent should react to webhooks, not to the conversation. The user saying "done" is a claim; `connected_card.updated` is a fact.

## When it fails

An expired link shows the user an expired screen, and every other refusal (revoked, used up, wrong device state) shows one generic screen that tells them to ask for a fresh link. Either way the fix is the same: create a new wallet link and text it.

## Sandbox behavior

With sandbox credentials, the connect code is always `111111` and the test card is `4242 4242 4242 4242` with any future expiry and any CVC. No real money moves.

Next: [Connect users](/wallet/connect-users)
