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

# Tracking intents

> Polling and (planned) webhooks for intent state.

<Warning>
  **The TypeScript SDK is WIP.** Today, poll [`GET /intent-details/:intentId`](/api/relayer) directly. Webhooks are planned but not yet shipped.
</Warning>

After submission, an intent moves through a small set of states. Today, you track via polling; the SDK's `subscribe` is a thin polling helper, and webhook delivery is on the roadmap.

## States

These come from the relayer's `IntentDetails.status` field.

**Non-terminal (intent is still in flight):**

| State     | Meaning                                                     | What's happening behind the scenes                                                                     |
| --------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `pending` | Intent created on-chain, awaiting solver bids               | Intent is in a batched plaintext broadcast to solvers; the auction is running                          |
| `solving` | Solver won the auction, fulfilling on destination chain     | Recipient bundle re-encrypted to the winning solver's key; solver is delivering to stealth address(es) |
| `settled` | Destination delivery confirmed, source settlement in flight | Attestation from the secure enclave is being verified on-chain to release the solver's reward          |

**Terminal (intent is done, no further state changes):**

| State       | Meaning                                                                             | Funds                                                           |
| ----------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| `completed` | Source settled. `settleTxHash` populated.                                           | Delivered to recipient on destination                           |
| `failed`    | Solver took the intent but failed to deliver before the protocol's execution window | **Refunded to sender** via the escape path on the source bridge |
| `expired`   | Auction window closed with no bidder                                                | **Refunded to sender**                                          |
| `cancelled` | Sender cancelled before a winner was picked                                         | **Refunded to sender**                                          |

None of the terminal-failure states (`failed`, `expired`, `cancelled`) lose funds. The bridge's escape path refunds the sender in all three cases, which is also why you can treat "no solver took it" as an expected failure mode rather than an emergency.

State transitions are forward-only. Once terminal, the intent is final.

## Poll

Direct API:

```ts theme={null}
const res = await fetch(`${RELAYER_URL}/intent-details/${intentId}`);
const { intent } = await res.json();
console.log(intent.status, intent.solveTxHash);
```

SDK (when published):

```ts theme={null}
const intent = await tachyon.intent.status(intentId);
```

For interactive UIs, poll every 2–5 seconds while the intent is live (`pending` or `solving`).

## Subscribe (polling helper)

```ts theme={null}
const unsubscribe = tachyon.intent.subscribe(intentId, (intent) => {
  if (intent.status === "completed") {
    console.log("done:", intent.settleTxHash);
    unsubscribe();
  }
}, { intervalMs: 3000 });
```

Under the hood this is a polling loop against `GET /intent-details/:intentId`. When webhooks ship, this method will switch to a long-lived connection automatically.

## Webhooks (planned)

<Note>
  WIP. The planned shape is shown for forward compatibility, none of these calls work today.
</Note>

```ts theme={null}
await tachyon.webhooks.register({
  url: "https://your-app.example.com/tachyon/webhook",
  events: ["intent.solving", "intent.settled", "intent.completed"],
  secret: process.env.WEBHOOK_SECRET,
});
```

Each callback will be signed with HMAC using `secret`:

```ts theme={null}
import { verifyTachyonSignature } from "@tachyon/sdk/webhooks";

app.post("/tachyon/webhook", express.raw({ type: "*/*" }), (req, res) => {
  const ok = verifyTachyonSignature(req.body, req.headers["x-tachyon-signature"], process.env.WEBHOOK_SECRET);
  if (!ok) return res.status(401).end();

  const event = JSON.parse(req.body.toString());
  switch (event.type) {
    case "intent.completed":
      markCompleted(event.intentId, event.settleTxHash);
      break;
  }
  res.status(200).end();
});
```

<Note>
  TODO: confirm webhook event names, payload shape, signature header, and retry policy before publish.
</Note>

## Choosing a method (today)

| Method                               | When to use                                                  |
| ------------------------------------ | ------------------------------------------------------------ |
| Poll `GET /intent-details/:intentId` | Backend job, batch worker, or any server flow                |
| SDK `subscribe` (polling helper)     | Interactive UI, same as poll, just less code                 |
| Webhooks                             | Will be the recommended server-to-server option once shipped |

## Idempotency (when webhooks ship)

Webhook deliveries can repeat on retries. Use the `eventId` in each callback to deduplicate before applying side effects.

## Failure semantics

* **No solver bids before auction closes** → intent transitions to `expired`, sender is refunded via the bridge's escape path.
* **Solver bid but failed to deliver in the protocol's execution window** → intent transitions to `failed`, sender is refunded.
* **Sender cancels before a winner is picked** → intent transitions to `cancelled`, sender is refunded.

Each of these is a handleable state, not an error. Surface the reason to the user and let them retry with wider parameters (higher reward, longer auction, looser `minAmountOut`, different chain pair).

<Card title="Recipient flow: claiming funds" icon="hand-holding-dollar" href="/sdk/retrieving-funds">
  How recipients sweep settled stealth-address balances.
</Card>
