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

# Webhooks and go-live

> Close the loop with webhooks, handle the pass lifecycle, and check off go-live

## Webhooks: closing the loop

Signed events keep your systems in step with what customers do in their wallets. Three event types exist today: `PASS_CREATED`, `PASS_STATUS_CHANGED`, and `TRANSACTION_CREATED`. Each is routed to your handler via the `X-Stell-Event` header. Create subscriptions [in the portal](/programs/webhooks). The full delivery contract, covering headers, payloads, and signature verification with code samples, is documented in [webhook deliveries](/programs/webhook-deliveries).

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant S as Stell
    participant E as Your endpoint
    S->>E: POST with X-Stell-Event, X-Stell-Delivery-Id, X-Stell-Signature
    E->>E: Verify signature, dedupe on delivery ID
    E-->>S: 2xx immediately, process async
    alt Non-2xx or timeout
        S->>E: Retry, up to 3 attempts per cycle
        Note over S: Failed events are re-driven in repeated cycles for up to ~24 hours
    end
```

The integration-level takeaways:

* **Verify, then acknowledge fast.** Check the signature, return a `2xx` immediately, and process the event asynchronously.
* **Delivery is at-least-once.** Deduplicate on `X-Stell-Delivery-Id` so a retried delivery is never processed twice.
* **Failures are retried, persistently.** Each delivery cycle makes up to 3 attempts with short backoff, and failed events are re-driven in repeated cycles for up to roughly 24 hours. An endpoint recovering from an outage will see many deliveries of the same event. The dedupe rule above absorbs exactly that.
* **No lookup needed.** `PASS_CREATED` and `PASS_STATUS_CHANGED` payloads carry your `externalId`, so the write-back to your customer record takes no extra call.
* **Subscribe to what you act on.** If your backend already knows it issued a pass, because it made the call, `PASS_CREATED` tells you nothing new. It earns its place when passes are also issued outside your integration, through the portal or an enrollment page.

## The pass lifecycle

Six statuses, delivered to you through the same webhook. Handle all six in your write-back logic:

| Status      | Meaning                                 |
| ----------- | --------------------------------------- |
| `PREACTIVE` | Issued, but not yet added to a wallet   |
| `ACTIVE`    | Live in the customer's wallet           |
| `INACTIVE`  | Removed from the wallet by the customer |
| `VOIDED`    | Voided by you                           |
| `REVOKED`   | Revoked, access withdrawn               |
| `EXPIRED`   | Past its validity date                  |

Two transitions dominate: `PREACTIVE → ACTIVE` when a customer adds the pass, and `ACTIVE → INACTIVE` when one removes it. Reflect both in your CRM so campaigns and support see wallet reality. For the merchant-facing view of the same lifecycle, see [pass lifecycle](/passes/lifecycle).

## Go-live checklist

* [ ] The API key is stored and used server-side only. It never reaches a browser, app, or client-side code.
* [ ] Add to Wallet is idempotent: every create is preceded by a lookup on `programId` + `externalId`.
* [ ] Wallet links are requested at click time and never cached, stored, or emailed as durable URLs.
* [ ] Your webhook endpoint verifies `X-Stell-Signature`, responds `2xx` quickly, and processes events asynchronously.
* [ ] Deliveries are deduplicated on `X-Stell-Delivery-Id`.
* [ ] All six pass statuses are handled in your write-back logic.
* [ ] The `externalId` mapping is tested end to end: create → add → webhook → write-back lands on the right customer record.
* [ ] The in-store payload, whether a template placeholder or the API `payload` field, points at the identifier your POS resolves, verified on a test pass.
* [ ] **NFC only:** your terminal fleet supports Apple VAS / Google Smart Tap with keys provisioned (confirmed with your terminal vendor), and an end-to-end tap is verified at a pilot store: tap → customer identifier at the POS → points accrual → pass update on the device.

Questions along the way? [Contact support](/support/contact).
