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

# Build a resilient event consumer

> Combine resumable SSE with a durable signed-webhook inbox and verify ordering, deduplication, and terminal outcomes.

SSE and webhooks solve different delivery problems. Use SSE for an attached
client that needs progress; use webhooks for a backend that must survive client
disconnects.

```mermaid theme={null}
sequenceDiagram
  participant P as Praxa run
  participant S as SSE consumer
  participant W as Webhook inbox
  participant A as Application state
  P-->>S: ordered progress events
  S->>A: update live view
  P-->>W: signed delivery
  W->>W: verify and deduplicate
  W->>A: commit durable transition
  S--xP: disconnect
  S->>P: reconnect with Last-Event-ID
```

## Prerequisites

Before you begin, prepare:

* disposable credentials with the exact read and write scopes used by the workflow;
* a persisted request or event identifier before the first network attempt;
* synthetic task, webhook, or event data plus a cleanup plan;
* storage for durable run, cursor, delivery, or receipt readback;
* an acceptance assertion that proves SSE and signed webhooks deduplicate, resume, and reconcile to one terminal state.

## 1. Persist the SSE cursor

Save the last processed event ID after your application commits the event. On
reconnection, send <code>Last-Event-ID</code> and require increasing event
sequences. A heartbeat is liveness, not a state transition.

## 2. Build a durable webhook inbox

Verify the signature over the exact raw body before parsing JSON. Within one
transaction or durable operation:

1. insert the immutable event ID with a unique constraint;
2. store the validated payload and delivery metadata;
3. enqueue or apply the application transition;
4. return 2xx only after durable acceptance.

A duplicate event ID should return success without repeating the effect.

## 3. Reconcile the two views

SSE and webhook arrival order can differ. Use the Praxa run ID and event
sequence as authority, not HTTP arrival time. When either lane observes a
terminal state, read the run if your product needs an authoritative final
projection.

## 4. Test failure paths

| Scenario              | Expected behavior                        |
| --------------------- | ---------------------------------------- |
| SSE disconnect        | Resume from the last committed event ID  |
| Duplicate webhook     | One logical application transition       |
| Modified body         | Signature rejection before JSON handling |
| Expired timestamp     | Rejection with no inbox insert           |
| Webhook handler crash | Non-2xx so delivery can retry            |
| EOF before terminal   | Incomplete state, not success            |
| Out-of-order delivery | Process by run sequence                  |

<CardGroup cols={2}>
  <Card title="SSE event reference" icon="wave-square" href="/fabric/api/events">
    Read framing, resumption, heartbeats, and terminal-event behavior.
  </Card>

  <Card title="Webhook tutorial" icon="webhook" href="/tutorials/webhooks">
    Implement signing, deduplication, replay, and cleanup.
  </Card>
</CardGroup>

## Troubleshooting

| Symptom                             | Resolution                                                                         |
| ----------------------------------- | ---------------------------------------------------------------------------------- |
| Admission is reported as completion | Follow the run, stream, delivery, or receipt to authoritative completion evidence. |
| Reconnect duplicates events         | Commit the cursor only after durable processing and deduplicate by event identity. |
| Retry creates duplicate work        | Reuse the original idempotency key and exact request body.                         |
| Failure is ambiguous                | Record unknown state and reconcile instead of starting new work.                   |

## Best practices

* Persist identity before I/O and state transitions after durable processing.
* Treat admission, delivery attempt, and cancellation request as non-terminal acknowledgements.
* Verify signatures against the raw body before parsing webhook JSON.
* Deduplicate streams and webhooks using stable event or delivery identity.
* Test disconnect, duplicate, out-of-order, timeout, revocation, and cleanup paths.

## Optimize for production

* Prefer event-driven updates while retaining bounded polling or readback reconciliation.
* Commit cursors in batches only when that cannot lose acknowledged application work.
* Keep webhook handlers short: verify, persist, acknowledge, then process asynchronously.
* Measure admission-to-terminal time, reconnect rate, duplicate rate, delivery latency, and reconciliation backlog.

Optimize only after the correctness and isolation matrix passes. Lower latency or cost is not an improvement if verified outcomes, authority checks, or recovery rates regress.

## Cleanup and next steps

1. Cancel or terminally reconcile disposable runs.
2. Disable test webhook endpoints and remove their signing secrets.
3. Delete synthetic inbox, cursor, and delivery records after assertions.
4. Revoke disposable credentials and keep only redacted lifecycle evidence.

After cleanup, run the [shared integration test matrix](/tutorials/test-your-integration) and record any environment-specific check that remains pending.

## Frequently asked questions

### What proves this tutorial works?

The minimum observable result is that SSE and signed webhooks deduplicate, resume, and reconcile to one terminal state. A compile, package import, mocked response, or initial admission alone does not prove the complete workflow.

### Can a browser, mobile app, or model prompt hold the credential?

No. The trusted runtime owns Praxa keys, OAuth tokens, and webhook secrets; clients receive only bounded application projections.

### How should an ambiguous mutation be retried?

Persist the exact logical input and idempotency key before the first attempt. Reconcile through authoritative readback or replay the exact request with that same key before creating new work.

### What should we monitor after release?

Monitor admission-to-terminal time, stream reconnects, duplicate events, webhook delivery latency, retries, and reconciliation backlog. Alert on authorization bypass, cross-tenant disclosure, repeated conflicts, or cleanup failure.
