Skip to main content
A managed consumer is a saved where predicate plus one processing checkpoint that smry keeps for you. Your agent polls, processes, acknowledges, and exits. It stores no cursor, no database row, no file — and it still never re-reads or misses an entry. This is the difference between a scheduled agent that needs infrastructure and one that needs an API key.

Which checkpoint owner to use

Both modes read the same ordered stream through POST /v1/feeds/changes. They differ in who remembers the position. Caller-managed cursors are covered in Follow sources. The rest of this page is the managed path. Create one consumer per independent processing loop — not per source. A single collection predicate can cover thousands of sources.

Create a consumer

POST /v1/feeds/consumers takes a name and an optional where. The predicate uses the same vocabulary as POST /v1/feeds/query; omit it to consume everything the account follows.
Response
smry canonicalizes the predicate: collection and source names become the ids they resolved to, so the stored where can differ from what you sent.
Creation is idempotent by name. Retrying with the same case-insensitive name and the same predicate returns the existing consumer, with its position intact — a retry after a timeout never resets progress. The same name with a different predicate is 409 consumer_name_taken. Delete it, reuse the original predicate, or pick another name.

A new consumer starts at now

A new consumer’s checkpoint is the account’s current high-water mark. It delivers future discoveries, not history. Its first poll normally returns an empty batch, and that is correct behaviour — not an empty account.
If you also need the entries that already exist, do it in this order:
1

Create the consumer first

From this moment forward, everything new is captured.
2

Then query the historical window

POST /v1/feeds/query with the same predicate and a since or where.discovered_after bound.
3

Then start polling

The consumer hands you everything discovered after step 1.
That order closes the race. Reversed, an entry discovered between the historical query and the consumer’s creation belongs to neither, and is lost silently.

Poll, process, acknowledge

Pass consumer_id to POST /v1/feeds/changes. Pass exactly one checkpoint: sending cursor and consumer_id together is a 400, and so is sending where with consumer_id — the consumer already stores its predicate.
Response
Then acknowledge that response’s next_cursor. The path carries the consumer id, so the body needs only the cursor.
Response

The rules that make this safe

  • Polling does not advance the checkpoint. Only an ack does. A batch is redelivered on every poll until its next_cursor is acknowledged. Crash halfway through and the next run gets the same events again.
  • Make your effects idempotent. Redelivery is the failure mode you are buying; at-least-once delivery is the guarantee, not exactly-once.
  • Acknowledge an empty batch too. ack_required stays true. Skipping the ack leaves the checkpoint where it was and the same window is re-scanned forever.
  • Acknowledge only the cursor the poll just handed you. A stale cursor, a cursor from another consumer, and an invented cursor are all rejected. Acknowledgements only move forward.
  • Acknowledging the same cursor twice is safe. It is the current position, so the call succeeds and only last_acknowledged_at moves.
  • Keep polling while has_more is true, acknowledging each batch before fetching the next. limit defaults to 100 and caps at 500.
  • Consumer progress is not human read state. Acking never marks anything read in the reader UI.

A complete polling loop

Both of these are the whole scheduled job: no local storage, safe to run on a cron, safe to kill at any point.

Inspect and remove consumers

GET /v1/feeds/consumers lists every consumer with its saved predicate, its timestamps, and pending — how many matching entries are waiting past its acknowledged checkpoint. A pending count that keeps climbing means a loop is polling without acking, or is not running.
Response
DELETE /v1/feeds/consumers/{id} removes only the saved predicate and the checkpoint. Followed sources, entries, and human read state are untouched.
Response
To change what a consumer watches, delete it and create a new one. The predicate is fixed for the life of a consumer, because its checkpoint is only meaningful against the predicate it was issued for.

Next

Follow sources

Discover, follow, query, and mutate the stream a consumer reads from.

Watch a page for changes

Feed a consumer from a page that publishes no feed at all.

Content safety

Every entry a consumer delivers was written by someone else.
Last modified on September 13, 2026