> ## 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.

# Hosted link

> No frontend? Create a wallet link and send the URL. We host the whole wallet.

This is the wallet for products with no frontend: CLIs, backend agents, email flows, anything that can deliver a URL. You create a wallet link and hand the URL to the user however you like. We host the page it opens.

## Create a wallet link

<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 looks like this:

```json theme={null}
{
  "object": "wallet_link",
  "id": "wl_...",
  "user_id": "USER_ID",
  "status": "active",
  "url": "https://app.agentcard.sh/w/...",
  "expires_at": "2026-08-11T20:15:00.000Z",
  "test_mode": true
}
```

## Deliver the URL

Print it in the terminal, put it in an email, drop it in a chat. The user opens it in any browser and gets the full wallet: add a card, see their cards, approve a payment.

## Link semantics

A link belongs to one user and expires after 15 minutes by default (`expires_in` accepts 60 to 86400 seconds). It can be opened up to 20 times within that window, so a user can tap it again without asking for a new one. Link previews and unfurl bots never consume an open; the page only exchanges the link when a real browser loads it.

When the user needs the wallet again later, create a fresh link. They are cheap and there's no limit on how many you create.

## Webhooks you will receive

* `wallet_link.opened` the first time the link is opened
* `connected_card.updated` when the user adds a card
* `transaction.*` events when payments happen

Don't poll the link for status. The webhooks are the record of what actually happened.

## When it fails

An expired link shows the user an expired screen. Every other refusal shows one generic screen asking them to request a fresh link. Your handling is one code path: create a new link and send 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)
