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

# Webhooks Overview

> Receive real-time event notifications from Fungies in your application.

Webhooks let your application receive real-time notifications when events occur in your Fungies account. Instead of polling the API, Fungies pushes event data directly to your server.

## Why Use Webhooks?

Webhooks are essential for building reactive integrations:

* **Fulfill orders automatically** - Deliver digital products when a payment succeeds
* **Sync with your database** - Keep your user records up-to-date with subscription changes
* **Trigger workflows** - Send confirmation emails, update inventory, or notify your team
* **Handle async events** - Respond to events that happen outside of direct API calls

## How It Works

```mermaid theme={null}
sequenceDiagram
    participant F as Fungies
    participant S as Your Server
    
    Note over F: Event occurs<br/>(e.g., payment_success)
    F->>S: POST /webhook (JSON payload)
    S-->>F: 200 OK
    Note over F: Delivery confirmed ✓
```

1. An event occurs (e.g., `payment_success`)
2. Fungies sends an HTTP POST request to your webhook URL
3. Your server processes the event and returns a `2xx` response
4. Fungies marks the delivery as successful

## Event Payload

Each webhook delivers a JSON payload containing an [Event object](/core-resources/event):

```json theme={null}
{
  "id": "evt_abc123",
  "type": "payment_success",
  "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
  "testMode": false,
  "data": {
    "order": {
      "object": "order",
      "id": "ord_xyz789"
      // ... order details
    }
  }
}
```

## Subscription checkouts and fulfillment

New subscriptions emit **separate events** from different stages of checkout. They can arrive in any order.

| Event                  | When it fires                                                                                  | Use for fulfillment?                                                                      |
| ---------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `subscription_created` | Stripe creates the subscription object (often while the first payment is still `PENDING`)      | **No** — informational only unless `data.lastPayment.status` is `PAID`                    |
| `payment_success`      | The initial or renewal payment is confirmed (`PAID`)                                           | **Yes** — grant access, deliver goods, sync your database                                 |
| `subscription_updated` | The subscription status changes (e.g. `incomplete` → `active` once the first payment confirms) | **Reconcile** — treat it as a prompt to re-check state, not as an ordered source of truth |

<Warning>
  Do not fulfill subscription purchases on `subscription_created` alone when `data.lastPayment.status`, `data.order.status`, or `data.subscription.status` is `PENDING` or `incomplete`. Wait for `payment_success`, or ignore the event until payment fields show `PAID` / `active`.
</Warning>

<Warning>
  **Webhook events can arrive out of order, and you cannot reorder them from the payload.** Delivery is asynchronous, retried, and batched, and the event payload carries no timestamp to sort by. Because `subscription_created` (`incomplete`) and `payment_success` (`active`) race, a stateless consumer can receive `active` and then `incomplete` for the same subscription.

  Handle this explicitly — do **not** derive the current state from whichever event happened to arrive last:

  * **Don't regress on stale events.** Never downgrade a subscription you have already seen as `active` / `PAID` because a later-delivered `subscription_created` or `subscription_updated` reports `incomplete`.
  * **Reconcile from the API when it matters.** A `subscription_updated` event tells you the status *changed*; to get the authoritative current status, retrieve the subscription with `GET /v0/subscriptions/{id}` rather than trusting the status on the last event you received.
</Warning>

Enable **`payment_success`** on every webhook endpoint that fulfills orders. If only `subscription_created` is selected, you will not receive a paid signal when checkout completes.

For renewals, use `payment_success` (initial and interval charges) or `subscription_interval` depending on your integration; the initial signup should always key off `payment_success` for the first charge.

## Delivery Guarantees

<Info>
  Fungies guarantees **at-least-once delivery** for all webhook events. Your endpoint should handle potential duplicate events idempotently.
</Info>

| Behavior          | Details                         |
| ----------------- | ------------------------------- |
| Protocol          | HTTPS (required for production) |
| Method            | POST                            |
| Expected response | `2xx` status code               |
| Retry attempts    | 5 times on failure              |
| Timeout           | 30 seconds                      |

## Securing Your Webhooks

Every webhook request includes an `x-fngs-signature` header containing an HMAC-SHA256 signature. Use your webhook secret to verify that requests actually came from Fungies.

```
x-fngs-signature: sha256_6808ed5be1262b60818359fa586145810d0793e8a677f1326520d3844e21b640
```

<Warning>
  Always verify webhook signatures in production. This prevents attackers from sending fake events to your endpoint.
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Set Up Webhooks" icon="gear" href="/developers/webhooks/setup">
    Create your webhook endpoint and register it with Fungies
  </Card>

  <Card title="Test Webhooks" icon="flask" href="/developers/webhooks/test">
    Test your integration locally with ngrok or webhook.site
  </Card>
</CardGroup>
