Polling

Polling is the alternative to webhooks for learning that a document is ready. Instead of Tediware pushing a notification to an endpoint you host, your system pulls processing outcomes from a feed on its own schedule. Use it when your platform cannot accept inbound HTTP requests, such as a firewalled or fetch-only system. When you can host an HTTPS endpoint, webhooks are recommended instead.

Polling and webhooks deliver the same outcomes. A feed entry carries only identifiers (resultId, traceGuid), never document content, so your system pulls the full data through the Results and Artifacts API exactly as it would after a webhook.

When to Use Polling

A partner is set to one delivery method or the other on its Configuration card:

  • Webhooks: Tediware builds the partner’s inbound, outbound, and error webhooks and pushes to them. Recommended when your system can receive HTTPS requests.
  • Polling: Tediware builds none of those webhooks. Your system pulls the partner’s inbound documents, outbound confirmations, and errors from the feed.

The feed is populated for every partner regardless of delivery method, so a partner on webhooks can also be polled, for reconciliation or as a backstop. Changing a partner’s delivery method rebuilds its inbound and outbound flows.

The Feed Endpoint

GET /platform/feed_entries
Authorization: Key <your-key>

The feed is an append-only stream of every deliverable document and error across your partners, ordered oldest first. Authenticate with an API key created on the API Keys page. The feed returns only your organization’s entries. Sandbox keys cannot read the feed; use a standard key.

Query Parameters

All parameters are optional. Without any, you get the oldest unread page of the whole feed.

  • partner: a partner key to filter by. An unknown key returns an empty page rather than an error.
  • direction: inbound or outbound.
  • status: success or error.
  • since: an ISO 8601 timestamp. Returns entries created at or after it. An invalid timestamp returns 400.
  • cursor: an opaque high-water mark from a previous page’s nextCursor. Advances past everything already returned.
  • limit: page size, default 50, capped at 100.

Response

{
  "feedEntries": [
    {
      "id": "8f1d3c2a-...",
      "direction": "inbound",
      "status": "success",
      "createdAt": "2026-06-08T14:22:33.482190Z",
      "partnerKey": "ACME",
      "traceGuid": "5ab72145-4a4b-40f6-95de-e2579f163f79",
      "resultId": "4c2021f8-8310-4628-80f9-b578d4d68e11",
      "detail": { "...": "..." }
    }
  ],
  "pagination": {
    "hasMore": true,
    "nextCursor": "MjAyNi0wNi0wOFQxNDoyMjozMy40ODIxOTBafDhmMWQzYzJh"
  }
}

Each entry contains:

  • id: the feed entry’s own identifier.
  • direction: inbound or outbound.
  • status: success or error.
  • createdAt: when the entry was written, ISO 8601 UTC with microseconds.
  • partnerKey: the key of the partner this document belongs to.
  • traceGuid: the trace identifier linking all results from the same processing run.
  • resultId: the processing result. Use it with GET /platform/results/:id to retrieve the full result and its artifacts.
  • detail: the result embedded inline, including the artifact ids and any error detail, so the entry is self-sufficient. Download document bytes with GET /platform/artifacts/:id.

The pagination block contains hasMore (whether more entries are waiting beyond this page) and nextCursor (the cursor to pass on your next request).

Cursor Pagination

The feed is a forward-only stream you advance with a cursor:

  1. Make the first request with no cursor to start from the beginning, or with since to start from a point in time.
  2. Process the returned entries and save pagination.nextCursor.
  3. Pass that value back as cursor on the next request to continue past what you already received.
  4. While pagination.hasMore is true, request the next page immediately, until it is false.
  5. When hasMore is false, wait your chosen interval, then poll again with the saved cursor.

Because the cursor is a high-water mark over (createdAt, id), an advancing poller never re-scans entries it has already seen. Persist the latest nextCursor so a restart resumes where it left off.

The Safety Lag

The newest entries are withheld from the feed for about 2 seconds before they become visible. This safety lag guarantees that an entry which commits slightly out of timestamp order can never be skipped by an advancing cursor, which makes polling lossless.

The practical consequence: a document you just processed may not appear for a moment. This is expected and is not a lost delivery; it will be on the next poll. Polling faster than every couple of seconds gains nothing, since anything newer than the lag window is withheld and those requests mostly return empty pages.

Choosing a Polling Interval

Pick an idle interval that matches how quickly you need documents. Anything from a few seconds to several minutes is reasonable, and most integrations are comfortable polling every minute or two. End-to-end latency is roughly your interval plus the 2-second safety lag. Whenever a response carries hasMore: true, drain the backlog with back-to-back requests rather than waiting out the interval between pages.

Handling Errors in the Feed

Errors are recorded per processing attempt, so a document that is retried can produce more than one error entry, each sharing the same traceGuid. If you want one error per document, deduplicate on traceGuid. Fetch the result behind an error entry with GET /platform/results/:id to read its errorMessage.

Rate Limits

The feed draws on the same per-key read budget as the rest of the Platform read API: 240 requests per minute. That budget is shared with result and artifact downloads, so the usual way to exceed it is a burst of artifact downloads after draining a large page, not the polling itself. When you exceed it, the API responds with 429 and a Retry-After header; wait that many seconds before retrying. See the Errors page for the full rate-limit response.

In-App Discovery

For a partner set to Polling, Tediware shows a poll card on the partner page (scoped to the partner) and on each flow page (scoped to the partner and that flow’s direction). Each card has a ready-to-copy curl command with the partner key already filled in, plus a link to create an API key. It is the fastest way to get the exact call for a partner.

Troubleshooting

  • The feed is empty: confirm the partner has processed at least one document, that you are using a standard (not sandbox) API key, and that your partner, direction, or status filters are not excluding everything.
  • A just-processed document is missing: this is the safety lag. Wait a couple of seconds and poll again.
  • The same error appears more than once: errors are per attempt. Deduplicate on traceGuid.
  • You keep seeing entries you already processed: you are not passing nextCursor back as cursor, or you are not persisting it across restarts.
  • 429 responses: you are exceeding the shared 240 requests per minute read budget, most often from artifact-download bursts. Back off for the Retry-After interval and spread downloads out.