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

# Pull feed

> Poll for your events instead of hosting a webhook receiver

<ParamField path="GET /api/v0/events" />

Poll for your events instead of hosting a receiver. The feed returns your canonical events oldest-first, filtered to your PULL subscription, with [cursor pagination](/webhooks/pagination).

Requires the `events` permission and one `ACTIVE` PULL subscription.

## End to end

<Steps>
  <Step title="Create a PULL subscription">
    `POST /api/v0/webhooks/subscriptions` with `"deliveryMode": "PULL"` and no `target`. It never delivers anything — it carries the `eventFilter` and `schemaVersion` this feed reads. One live PULL subscription per partner. See [Create a PULL subscription](/webhooks/subscriptions#create-a-pull-subscription).
  </Step>

  <Step title="Poll this endpoint">
    Send your API key and secret, optionally with `types` and `limit`.
  </Step>

  <Step title="Read the page">
    Take `data.events` — oldest first — and `data.nextCursor`.
  </Step>

  <Step title="Paginate">
    Call again with `?after=<nextCursor>` until `nextCursor` is `null`. A short page that still carries a cursor is normal, so keep going.
  </Step>

  <Step title="Checkpoint and deduplicate">
    Persist the cursor, and deduplicate on `eventId`. Pull responses are **not** signed.
  </Step>
</Steps>

<Warning>
  With no active PULL subscription, this endpoint returns `404` — not an empty list.
</Warning>

## Query parameters

<ParamField query="types" type="string">
  Narrows your subscription's filter. Repeatable. It cannot widen the filter beyond what the subscription allows.
</ParamField>

<ParamField query="after" type="string">Pagination cursor.</ParamField>
<ParamField query="limit" type="integer" default="25">Page size, maximum 100.</ParamField>

Returns `data.events[]` oldest first, `data.count`, and `data.nextCursor`.

<CodeGroup>
  ```bash Request theme={null}
  curl -G https://qa-api.endl.xyz/api/v0/events \
    -H "X-API-KEY: $ENDL_API_KEY" -H "X-API-SECRET: $ENDL_API_SECRET" \
    --data-urlencode "types=payout.completed" --data-urlencode "limit=25"
  ```

  ```json 200 OK theme={null}
  {
    "code": 200,
    "message": "Success",
    "status": "SUCCESS",
    "data": {
      "events": [
        {
          "eventId": "evt_9f1a4c7b2e08",
          "eventType": "payout.completed",
          "eventCreatedAt": "2026-09-02T14:03:11.482Z",
          "version": "1.0",
          "data": {
            "referenceId": "c2608cc2-084b-41d1-8be2-c4f3abd55e5a",
            "referenceType": "TRANSACTION",
            "amount": "1500.00"
          }
        }
      ],
      "count": 1,
      "nextCursor": "eyJ1IjoiRVZFTlRTIn0.a3f9c1e8d0"
    },
    "errors": []
  }
  ```
</CodeGroup>

<Note>
  **Same projection as PUSH.** The feed renders through the exact projection PUSH signs, with every leaf value as a string, so what you poll reconciles with what a PUSH delivery would have contained.
</Note>

<Accordion title="Status codes">
  `200` · `400` bad `limit`, missing `events` permission, or `INVALID_CURSOR` · `401` authentication · `404` no active PULL subscription · `503` directory unavailable
</Accordion>
