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

# Result Delivery

> Choose between pull, webhook push, and ping+pull methods for receiving verification results.

# Result Delivery

Zenoo supports three methods for delivering verification results. Choose based on your infrastructure, latency requirements, and integration complexity.

<Tabs>
  <Tab title="Pull via Token">
    ## Pull via Token

    The simplest approach. After initiating a verification, poll the pull endpoint until results are ready.

    ```mermaid theme={null}
    sequenceDiagram
        participant Y as Your System
        participant Z as Zenoo
        Y->>Z: POST /init
        Z-->>Y: 200 OK + tokens (pull, start)
        Y->>Z: GET /sharable-payload/{pull}
        Z-->>Y: 204 No Content (still processing)
        Y->>Z: GET /sharable-payload/{pull}
        Z-->>Y: 200 OK + full results
    ```

    **How it works:**

    1. Initiate a verification (sync or async). Save the `pull` token from the response.
    2. Poll `GET /sharable-payload/{pull_token}` at intervals.
    3. When results are ready, the endpoint returns `200` with the full payload.

    ### Polling Strategy

    | Time Window      | Poll Interval    | Rationale                                                           |
    | ---------------- | ---------------- | ------------------------------------------------------------------- |
    | 0 to 2 minutes   | Every 10 seconds | Most automated checks (screening, registry) complete in this window |
    | 2 to 10 minutes  | Every 30 seconds | Allows for slower providers or queued checks                        |
    | After 10 minutes | Stop polling     | Switch to webhook or contact support                                |

    ```javascript poll-results.js theme={null}
    async function pollResults(pullToken) {
      const maxAttempts = 30;

      for (let i = 0; i < maxAttempts; i++) {
        const response = await fetch(
          `https://instance.prod.onboardapp.io/api/gateway/sharable-payload/${pullToken}`
        );

        if (response.status === 200) return await response.json();
        if (response.status === 404) throw new Error("Token invalid or expired");

        // 204: still processing
        const delay = i < 12 ? 10_000 : 30_000;
        await new Promise((r) => setTimeout(r, delay));
      }

      throw new Error("Polling timeout. Results not ready after 10 minutes.");
    }
    ```

    ### Pull Endpoint Response Codes

    | Status | Meaning                                                                  |
    | ------ | ------------------------------------------------------------------------ |
    | `200`  | Results are ready. Response body contains the full verification payload. |
    | `204`  | Still processing. Retry after a short delay.                             |
    | `404`  | Token is invalid or has expired.                                         |

    ### Pull Token Lifetime

    <Note>
      Pull tokens remain valid for **30 days** after results become available. After that, the token expires and returns `404`. Store results in your own system before the token expires.
    </Note>
  </Tab>

  <Tab title="Webhook Push">
    ## Webhook Push

    Zenoo pushes the full verification results to your endpoint as soon as they are ready. No polling needed.

    ```mermaid theme={null}
    sequenceDiagram
        participant Y as Your System
        participant Z as Zenoo
        Y->>Z: POST /init
        Z-->>Y: 200 OK + tokens
        Note over Z: Processing...
        Z->>Y: POST /your-webhook (verification.completed + full results)
        Y-->>Z: 200 OK
    ```

    **How it works:**

    1. Configure a webhook endpoint during onboarding.
    2. Initiate a verification.
    3. Zenoo POSTs the `verification.completed` event with full results to your endpoint.
    4. Your endpoint returns `200` within 30 seconds.

    This is the fastest delivery method. Results arrive the moment processing finishes, with no polling delay.

    **Requirements:**

    * HTTPS endpoint accessible from the internet.
    * Signature verification on every incoming webhook.
    * Response within 30 seconds.
    * Idempotent event handling (Zenoo may deliver the same event more than once).

    See the [Webhooks](/guides/webhooks) guide for full setup instructions.
  </Tab>

  <Tab title="Ping + Pull">
    ## Ping + Pull

    A lightweight webhook notification tells you results are ready. You then fetch the full payload at your convenience.

    ```mermaid theme={null}
    sequenceDiagram
        participant Y as Your System
        participant Z as Zenoo
        Y->>Z: POST /init
        Z-->>Y: 200 OK + tokens
        Note over Z: Processing...
        Z->>Y: POST /your-webhook (status + pull_token only)
        Y-->>Z: 200 OK
        Y->>Z: GET /sharable-payload/{pull_token}
        Z-->>Y: 200 OK + full results
    ```

    **How it works:**

    1. Configure a webhook endpoint.
    2. Initiate a verification.
    3. Zenoo sends a small notification (status + pull token, no result data).
    4. Your system fetches full results from the pull endpoint when ready.

    This keeps webhook payloads small and decouples notification from data retrieval. Your system controls when and how it fetches results.

    **Notification payload:**

    ```json theme={null}
    {
      "event_type": "verification.completed",
      "status": "complete",
      "callback_reference": "APP-2026-0123",
      "pull_token": "eyJhbGciOiJIUzI1NiJ9.pull-token...",
      "completed_at": "2026-01-15T14:35:00Z"
    }
    ```

    **Then fetch results:**

    ```bash theme={null}
    curl "https://instance.prod.onboardapp.io/api/gateway/sharable-payload/{pull_token}"
    ```
  </Tab>
</Tabs>

## Comparison

| Aspect           | Pull via Token                       | Webhook Push                                     | Ping + Pull                                         |
| ---------------- | ------------------------------------ | ------------------------------------------------ | --------------------------------------------------- |
| **Mechanism**    | Poll endpoint at intervals           | Full results pushed to your endpoint             | Lightweight notification, you fetch results         |
| **Latency**      | Depends on poll interval (10-30s)    | Near-instant                                     | Near-instant notification, you control fetch timing |
| **Complexity**   | Lowest                               | Medium (webhook endpoint + sig verification)     | Medium (webhook + pull call)                        |
| **Reliability**  | High (you control timing)            | Depends on webhook delivery                      | High (notification + guaranteed pull)               |
| **Payload size** | N/A (you fetch on demand)            | Full results in webhook                          | Small notification, full results on pull            |
| **Best for**     | POC, low-volume, simple integrations | Real-time processing, event-driven architectures | Production systems, high-reliability requirements   |

## Recommendation

<Tip>
  For production, use Ping + Pull. You get real-time notification that results are available, with the flexibility to fetch full results on your own schedule. Small webhook payloads are more reliable to deliver, and you always have the pull endpoint as a fallback.
</Tip>

**For POC and development:** Use Pull via Token. It requires no webhook infrastructure. Just initiate a verification and poll for results.

**For event-driven architectures:** Use Webhook Push. If your system is already built around event processing (message queues, serverless functions), receiving full results via webhook fits naturally.

### Combining Methods

The most resilient pattern uses webhooks with polling as a fallback:

1. Configure webhooks for instant notification (push or ping+pull).
2. Implement a background job that polls for any verifications that have been pending longer than expected.
3. If a webhook is missed (endpoint down, network issue), the polling job catches it.

```javascript fallback-poller.js theme={null}
// Background job: catch any missed webhooks
async function checkPendingVerifications() {
  const pending = await db.verifications.find({
    status: "pending",
    initiated_at: { $lt: new Date(Date.now() - 10 * 60 * 1000) } // >10 min ago
  });

  for (const v of pending) {
    const response = await fetch(
      `https://instance.prod.onboardapp.io/api/gateway/sharable-payload/${v.pull_token}`
    );
    if (response.status === 200) {
      const results = await response.json();
      await processResults(v.id, results);
    }
  }
}
```

## Next Steps

* [Webhooks](/guides/webhooks) -- Set up webhook endpoints and signature verification
* [Sync vs Async Flows](/guides/sync-vs-async) -- Choose between sync and async execution
* [Error Handling](/guides/error-handling) -- Handle failures and timeouts during result retrieval
