# Ephemeral containers and claim

Your agent can connect a user who has no Wire account. They get a fully working ephemeral container with a 7-day lifetime: same tools, same MCP endpoint, no signup. When they're ready to keep it, the SDK upgrades the container to permanent without the user ever leaving your agent.

This is the no-account onboarding flow: connect first, let the user try things, prompt to claim before the container expires.

## Know when a container is ephemeral

Two surfaces tell you:

- `connection.expiresAt` is non-null on an ephemeral connection and tells you when the container lapses.
- `getStatus(apiKey)` returns `container.isEphemeral` and `container.ephemeralExpiresAt`, so you can check at any time and decide when to prompt.

## Claim, the blocking way

<Tabs syncKey="lang">
  <TabItem label="TypeScript">
    ```typescript
    const claimed = await client.claim(connection.apiKey, {
      onUserPrompt: ({ url }) => {
        myUi.show(`Sign up to keep your container: ${url}`);
      },
    });
    // claimed.expiresAt === null, the container is permanent
    ```
  </TabItem>
</Tabs>

`claim()` mints a single-use claim link, hands it to your `onUserPrompt` handler (or prints it and opens the OS browser if you don't pass one), and resolves once the user finishes signing up. All their data, the MCP URL, and the apiKey stay exactly the same.

Semantics worth knowing:

- Calling `claim()` on an already-claimed container resolves immediately with the current snapshot. It's safe to call twice.
- If the user doesn't finish within five minutes (configurable via `timeoutMs`), `claim()` throws `CLAIM_TIMEOUT`. The link itself stays valid for 30 minutes, so the user can still finish in the browser, and a later `getStatus()` will show the container as permanent.

## Claim, the non-blocking way

Turn-based agents shouldn't hold a promise open while a user signs up. Mint the link, show it, and detect completion on your own schedule:

<Tabs syncKey="lang">
  <TabItem label="TypeScript">
    ```typescript
    const { url, expiresAt } = await client.getClaimUrl(connection.apiKey);
    myUi.show(`Sign up to keep your container: ${url}`);

    // Any later turn:
    const status = await client.getStatus(connection.apiKey);
    if (!status.container.isEphemeral) {
      // claimed, celebrate accordingly
    }
    ```
  </TabItem>
</Tabs>

`getClaimUrl()` mints the link and nothing else. It throws `ALREADY_CLAIMED` if the container is already permanent, which doubles as a cheap "does this still need claiming" check.

## Timing the prompt

`ephemeralExpiresAt` gives you the deadline. A pattern that works well: prompt once when meaningful data has accumulated (the user now has something to lose), then again as expiry approaches. Avoid prompting on first contact; the whole point of the flow is that the user experiences value before being asked to sign up.