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

# redeem()

> Redeem a previously-claimed offer — phase 2 of the two-phase flow. Resolves the pending transaction internally (placement → latest; no transaction handle), renders the redemption screen, and on the user's CTA tap opens the advertiser tab. Returns a bare RedeemResult and never rejects. Includes placement(id).redeem().

Redeems a transaction that [`show()`](/publishers/web/sdk-reference/show) recorded in
**deferred** mode. It renders the redemption screen for the pending transaction, and on the
user's CTA tap opens the advertiser in a new tab (synchronously, inside the click gesture, to
dodge popup blockers) and emits the provisional grant. This is **phase 2** of the two-phase flow.

## When to use this

Call `redeem()` after a deferred [`show()`](/publishers/web/sdk-reference/show) resolved
`claimed` and your own conversion / save step has run. Call it **in response to a user gesture**
(e.g. a button click) so the advertiser tab opens reliably.

Do not call `redeem()` in [immediate mode](/publishers/web/guides/redemption-modes) — the
transaction is already redeemed inline and `redeem()` resolves `none`.

## Signature

```typescript theme={null}
function redeem(options?: RedeemOptions): Promise<RedeemResult>
```

### RedeemOptions

```typescript theme={null}
interface RedeemOptions {
  placement?: string;                                      // resolve the transaction made at this placement
  host?: { name: string; status?: string; logoUrl?: string }; // optional host product card rendered as "Active"
}
```

| Field       | Type                                                  | Required | Description                                                                                                                                     |
| ----------- | ----------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `placement` | `string`                                              | No       | Resolve the transaction claimed at this [placement](/publishers/web/sdk-reference/placement); never crosses to another placement's transaction. |
| `host`      | `{ name: string; status?: string; logoUrl?: string }` | No       | Host product card rendered as "Active" on the redemption screen.                                                                                |

There is no `transactionId` option — no transaction handle ever crosses the API. `redeem()`
resolves the pending transaction **internally**:

1. **`placement`** — the transaction claimed at that placement; never crosses to another
   placement (resolves `none` if that placement has none).
2. **Otherwise** — the single most-recent pending transaction for this user.

Your code never needs to check whether anything is pending: call `redeem()` when your flow
is ready, and it resolves `{ status: 'none' }` if there is nothing to redeem.

## RedeemResult

```typescript theme={null}
type RedeemResult =
  | { status: 'redeemed' }                              // advertiser redirect fired for the pending transaction
  | { status: 'dismissed'; reason?: NotGrantedReason }  // sheet closed without redeeming — see below
  | { status: 'expired' }                               // reserved — see note below
  | { status: 'none' };                                 // STRICTLY: no live pending transaction to redeem
```

The arms carry **no transaction payload**. `redeem()` **never rejects** — the error contract
mirrors `show()`: a user-driven close resolves `dismissed` (no reason or a user reason), and an
internal failure resolves `dismissed` with `reason: { type: 'error', error }`. On an error the
pending transaction is untouched, so calling `redeem()` again later is safe. `none` always means
exactly "nothing pending — don't retry"; it is never an error in disguise.

<Note>
  `expired` is part of the type union but is **not emitted by the current SDK**: an expired
  transaction (past its \~7-day TTL) fails resolution and is returned as `none`. Handle `none` as
  "nothing to redeem"; treat `expired` as reserved for a future change.
</Note>

## Examples

### Redeem the latest claim

```javascript theme={null}
import Encore from '@encorekit/web-sdk';

const result = await Encore.redeem();
if (result.status === 'redeemed') {
  // Advertiser tab opened; provisional grant sent; transaction marked redeemed.
} else if (result.status === 'none') {
  // Nothing pending — claim an offer with show() first. (Not retryable.)
} else if (result.status === 'dismissed' && result.reason?.type === 'error') {
  // Internal failure — the pending transaction is untouched; safe to retry later.
}
```

### Two-phase flow, no handle

```javascript theme={null}
const shown = await Encore.show();
if (shown.status === 'claimed') {
  // ... your conversion runs ...
  await Encore.redeem(); // resolves the pending transaction internally
}
```

Pending transactions are stored locally with a \~7-day TTL, so the deferred flow survives a
page reload: after the reload, calling `redeem()` still resolves the claim.

## placement(id).redeem()

Redeems the transaction claimed at a named [placement](/publishers/web/sdk-reference/placement),
equivalent to `Encore.redeem({ placement: id })`. It never crosses to another placement's
transaction.

```javascript theme={null}
// Phase 1 — claim at a named placement
await Encore.placement('cancel_flow').show();

// Phase 2 — redeem that placement's transaction
const outcome = await Encore.placement('cancel_flow').redeem();
```

## Gotchas

* **Open the tab inside the gesture.** The advertiser tab is opened synchronously on the CTA
  tap. Trigger `redeem()` from a user action, not a timer or a detached promise, or popup
  blockers may swallow the redirect.
* **Expired claims resolve as `none`.** A transaction past its \~7-day TTL fails resolution and
  comes back as `none` (not `expired`) in the current SDK — see the note above.
* **TRANSACTION\_ID substitution.** If the offer's `advertiserUrl` contains the literal
  `TRANSACTION_ID`, it is replaced with the transaction id when the redirect fires, and the
  offer's `trackingParameters` are appended.
* **`redeemed` makes no conversion claim.** It means the redirect fired — advertiser
  conversion happens later, asynchronously. Verify conversions and entitlements server-side,
  by user, via the
  Encore’s server-side verification —
  never from the client.

## Related

* [`show()`](/publishers/web/sdk-reference/show) — phase 1 of the flow.
* [Redemption Modes](/publishers/web/guides/redemption-modes) — full walkthrough, the Transaction shape, and why deferred needs `redeem()` while immediate doesn't.
* [placement](/publishers/web/sdk-reference/placement) — named placements.
