Polling Fulfillment Requests
The GET /fulfillment-requests endpoint is how a Source discovers new work (fulfillment requests and fulfillment orders assigned to it). This guide covers the right way to poll it — and how to avoid the single most expensive mistake we see: rebuilding Flxpoint's state locally.
Why this page exists:multiple integrators have independently built full-mirror systems — polling every FR every cycle, diffing against a local database, generating their own change feed. One customer explicitly described maintaining a mirror copy. That approach burns API quota, adds latency, and introduces staleness. There's a better way.
The short version
- Poll
GET /fulfillment-requests?status=NEWevery 60–300 seconds. - For each FR you haven't seen before, acknowledge it via
POST /fulfillment-requests/{id}/acknowledge. - Track the
idvalues you've processed in your own storage — that's the dedupe key. - Use
statusfilters to get only what you need. Never pull the full dataset on every cycle.
Already polling for order-level changes? Use GET /orders instead
Recent addition.GET /ordersnow acceptsorderUpdatedAt,fulfillmentRequestsUpdatedAt, andshipmentsUpdatedAtas ISO-8601 timestamps. Results are scoped to records updated afterthe cursor — across the order, its FRs, and its shipments in one call. If your reason for polling FRs was really "detect any change on any open order", this replaces the FR poll entirely. For the exact dated history of this endpoint, see its changelog inside Stoplight.
# Once per minute, pick up everything that changed since last cursor.
curl -G "https://api.flxpoint.com/orders" \
-H "X-API-TOKEN: YOUR_TOKEN" \
--data-urlencode "orderUpdatedAt=${LAST_SEEN}" \
--data-urlencode "fulfillmentRequestsUpdatedAt=${LAST_SEEN}" \
--data-urlencode "shipmentsUpdatedAt=${LAST_SEEN}"
# Advance LAST_SEEN to the max updated timestamp in the response
# before the next call. No local processed-set needed.Use this when your integration is an order-tracking system, a reporting mirror, or a cross-system reconciliation job. Use the FR-specific flow (below) when you're the supplier actively fulfilling requests — the FR state machine (acknowledge, cancel, ship) is still how new work is claimed.
What the endpoint does (and doesn't) give you
The endpoint supports filters by status and pagination by page / size. It does not currently support an updatedAfter / modifiedSince timestamp filter — a feature request tracked by the platform team.
Until that parameter exists, combine two mechanisms:
- Status filter to narrow the working set. Most integrations care about one of a few states (NEW, ACKNOWLEDGED, CANCELLED). Filter server-side.
- Local processed-set for dedupe. A single column (
flxpoint_fr_id TEXT PRIMARY KEY) is enough. On each poll, skip any ID already in the set.
Polling cadence
| Volume | Recommended poll interval | Notes |
|---|---|---|
| < 100 FRs / day | 5 minutes | Default safe. |
| 100 – 1,000 FRs / day | 1 – 2 minutes | Still well within 2 req/s per source token. |
| 1,000+ FRs / day | 60 seconds, pagination active | Use size=100; only paginate when the first page comes back full. |
Polling more often than every 30 seconds rarely helps — Flxpoint's own upstream workflows don't propagate state that fast in the normal case.
The right implementation
// Pseudo-code — adapt to your stack.
async function pullNewFRs() {
const processed = await db.loadProcessedIds(); // Set<string>
let page = 1;
while (true) {
const res = await flxGet(`/fulfillment-requests?status=NEW&page=${page}&size=100`);
const body = await res.json();
const items = body.items ?? [];
if (items.length === 0) return;
for (const fr of items) {
if (processed.has(String(fr.id))) continue; // dedupe
await handleFR(fr); // your business logic
await db.markProcessed(fr.id); // atomic, idempotent
}
if (items.length < 100) return; // last page
page++;
}
}
// Call every N seconds from a scheduler, cron, or worker queue.
// A single caller per token — do not parallelize pulls on the same token.Acknowledgement is your state machine
Once you've acknowledged an FR, it moves out of NEW on the Flxpoint side. That's your natural fence — next time you poll status=NEW, it won't come back. Treat acknowledgement as the single source of truth for "I've got this", not a local flag.
// Acknowledge as soon as you've durably persisted the FR
// to your side. If your handler crashes before ack, the FR
// will reappear on the next poll — that's a feature, not a bug.
await flxPost(`/fulfillment-requests/${fr.id}/acknowledge`);Idempotency: handle replays safely
Because acknowledgement is the fence, and because your poller might retry after a crash, every handler needs to be idempotent on fr.id. Two implementations that work:
- Unique constraint on your side.
INSERT ... ON CONFLICT (flxpoint_fr_id) DO NOTHING. Second insert is a no-op. - Check-then-act inside a transaction. Look up the FR ID, skip if found, otherwise insert + downstream side effects in one transaction.
Anti-patterns we've seen
- Full mirror syncs. Pulling all FRs on every cycle to diff against a local snapshot. Wastes quota, increases latency, introduces a second source of truth. Filter by
statusand dedupe locally instead. - Polling without filters.
GET /fulfillment-requestswith no status filter returns everything visible to the token. Always pass a status. - Parallel pollers on the same token. Two processes polling the same source token will split the 2 req/s budget and both stall on pagination. One poller per token. Use a leader-election or a single worker.
- Retrying 4xx errors. A 400 on acknowledgement (e.g. trying to ack a cancelled FR) will never succeed — log it, move on, don't retry-loop.
- Processing before acknowledgement. If your handler sends data to a third-party system (an ERP, a carrier label service), wrap it in a local transaction and only ack Flxpoint after it commits. Otherwise a crash leaves you with a processed-downstream-but-unacked FR that reappears on the next poll.
When updatedAfter ships
If a filterUpdatedAfter parameter lands, the flow becomes simpler — poll with updatedAfter=<last-seen> and drop the local processed-set to just a timestamp. This guide will be updated when that happens. Subscribe to the Fulfillment Request resource page for endpoint-level changes.
Related
- Fulfillment Request — endpoint reference
- Authentication & Rate Limits (why one token per source)
- Integrating as a Source (the full Source-side flow)