# GET /v1/events — the event log

Everything that happens to your programme, as a retrievable list — poll it to reconcile your
ledger, catch a delivery your endpoint missed, and learn when a waitlisted user gets a seat.
When push webhooks ship, they will deliver these same events; building on this log now means
changing nothing later.

```http
GET /v1/events?product_id=<your app id>&limit=25
Authorization: Bearer {your_api_key}
```

Reply: `{ "events": [ { "id", "type", "createdAt", "data" } ], "hasMore": true }` — newest
first. Page with `starting_after=<the last event's id>`. Every `data` carries
`productUserId`; the rest is per type:

| `type` | When | `data` |
|---|---|---|
| `user.enrolled` | A user was enrolled (first call only) | `seatState` |
| `seat.opened` | A waitlisted user was promoted to an earning seat | — |
| `binding.created` | An X account was connected — a first qualifying post, or a member approving a waiting account | `platform`, `handle` |
| `binding.pending` | An account posted the code and is waiting for approval on the rewards page | `platform`, `handle` |
| `binding.removed` | An X account was disconnected — by the user on the rewards page, or by you as the owner | `platforms` |
| `post.verified` | A post passed verification and was credited | `amount`, `rewardType`, `postedAt`, `postUrl` |
| `reward.delivered` | Your endpoint accepted a delivery | `rewardReference`, `amount`, `rewardType` |
| `reward.revoked` | A credited post was deleted or its disclosure edited out before the day-5 settlement. **Also pushed to your reward endpoint** as `kind: "revocation"`, so this row is audit rather than the way you find out | `rewardReference`, `amount`, `reason` (`post_deleted` \| `disclosure_removed`), `postUrl` |
| `reward.delivery_failed` | A delivery exhausted its retries (seven over ~3 days) without a 2xx | `rewardReference`, `amount`, `attempts` |

Reconciliation recipe: nightly, walk events since your last stored cursor; every
`reward.delivered` should exist in your rewards table by `rewardReference`. A
`reward.delivery_failed` is a reward your endpoint never accepted — but it is not stranded:
once your endpoint answers any delivery with 2xx again, failed rewards are requeued
automatically and arrive on their own. Crediting from the event is still sanctioned
belt-and-braces (idempotent by reference; a later redelivery dedupes into a 2xx), and
`X-Earnesty-Pending` on each successful delivery tells you the backlog size — but with
automatic recovery, this log is an audit tool, not a correctness requirement.

`product_id` is the App ID — the same value the guide's constants table calls App ID; the
query parameter keeps the older name.

Errors are `{ "error", "code" }`: `missing_field`, `invalid_limit`, `invalid_cursor` (400),
`unauthorized` (401), `app_not_found` (404).