POKE architecture

POKE architecture (Publish Once, Kickoff Everywhere) is a lightweight event-driven design for APIs. Every change a consumer cares about is published once to an append-only event log per customer. Agents and integrations read that log from a cursor or get it pushed as webhooks, instead of polling your API.

Ask your agent
Is my service POKE? Check svix.com/POKE.md

POKE.md is a skill for coding agents. It audits your service against the POKE tenets and ranks the endpoints consumers are most likely polling. Say “make it POKE” and it implements the top next steps.

What you get

What POKE architecture gives you

Agents and integrations find out what changed, and when slow work finished, without polling your API for it.

Real-time events and context

Consumers react the moment something changes, either from a push or by polling a cursor that only returns what's new.

Agent friendly

An agent subscribes to the events it needs with one API call, no dashboard or human involved. Local and ephemeral agents that go offline catch up from their cursor when they're back.

Scalable and cheap to operate

Reading what's new is one indexed range query, not a full re-fetch. Throughput grows while latency stays flat, and agent traffic stops multiplying your infrastructure.

Easy to adopt

An append-only table and an endpoint next to your existing API, added one resource at a time. No broker, no rewrite.

How it works

How POKE architecture works

Your service writes each event once, to its customer's log, in the same transaction as the change it describes. Every consumer keeps a cursor: the ID of the last event it read. To catch up it asks for everything after its cursor, over plain HTTP polling or server-sent events (SSE), or it receives the same events as signed webhooks.

The log sits next to the API you already have, so there's no broker to run and no rewrite. You can start with a single endpoint.

An agent waiting for one email to be deliveredWithout POKE
→ GET /emails/em_31← "status": "sending"
→ GET /emails/em_31← "status": "sending"
→ GET /emails/em_31← "status": "sending"
… every 10 seconds, for every email
→ GET /emails/em_31← "status": "delivered"

Dozens of requests per email, almost all of them answering “nothing changed”, and the agent still finds out up to 10 seconds late.

The same agent, reading from its cursorWith POKE
→ GET /events?after=evt_1041← evt_1042 email.delivered em_31

One request returns every change since the last read, for every email the agent is watching. Hold it open with SSE, or subscribe to webhooks, and the event arrives the moment it's written.

Acme's agent has read up to event 4, so its next request returns 7 and 9. Each customer only ever sees its own lane, and endpoints can start publishing to the log one at a time.
Core tenets

The four core tenets of POKE architecture

Every POKE service holds to these four. Everything else builds on them.

Simple

A single append-only event store, with a log per customer. No new broker, framework, or service is needed just to emit events.

Reliable

A consumer can resume from any cursor and never misses or reorders an event, even across disconnects and concurrent writes.

Secure

Events are scoped per customer. Pulling requires the customer's auth, and pushed deliveries are signed.

Interoperable

Pull is plain HTTP. Push follows Standard Webhooks. No proprietary client is needed.

Additional goals

Beyond the benefits above, a complete POKE service also does these. Missing one isn't a failure, but each makes the stream more useful.

Push and pull from the same log
Webhooks and the pull API share event IDs and ordering, so a consumer that misses a push recovers by pulling.
Bootstrap events
A new consumer can request synthetic events for existing state, like contact.created for every contact, and build its view from the stream.
Async operations
Long jobs emit completion and failure events on the same log, so nobody has to poll the job's status endpoint.
Observability
Customers can see delivery attempts, failures and how far behind each consumer is.
Implementation

How to implement POKE architecture

Start with the API you'd build anyway, then add the event log beside it.

  1. 1Identify slow and async operations, like sending an email or rendering a video.
  2. 2Identify the events your customers should react to, like email.failed or user.signed_up.
  3. 3Create an append-only log of events per customer. A Postgres table is a fine place to start.
  4. 4Expose it: a cursor-based endpoint to read the log (with SSE or long-polling) that filters by event type, then webhooks to push the same events.

Watch out for the Postgres cursor race

A bigserial ID is assigned when a row is inserted, but the row only becomes visible when its transaction commits. With two concurrent writers, a reader can see event 102, move its cursor past 101, and then 101 commits behind it and is never read.

Fix it by serializing inserts per customer with an advisory lock, or by having a single writer assign the IDs, for example one consumer draining a stream. POKE.md covers both, plus a block-on-read variant and the Kafka equivalent.

Lock on write: one writer per customer at a timeSQL
-- Insert events (customer_id = 7)
BEGIN;
SELECT pg_advisory_xact_lock(7);

INSERT INTO events (
  customer_id, event_type, payload
) VALUES (
  7, 'contact.updated',
  '{"contact_id": 42}'
);
COMMIT;

-- Fetch events
SELECT id, event_type, payload
FROM events
WHERE customer_id = :customer_id
  AND id > :last_id
ORDER BY id
LIMIT 100;

Full or thin payloads?

Full payloads carry the whole resource, so consumers can act without fetching anything, which agents benefit from most. They can go stale, and they can leak fields a subscriber isn't allowed to see. Thin payloads carry only IDs, which keeps them fresh and permission-safe but forces a fetch for every event.

A good default is in between: include enough to act on (identifiers, changed fields, a version or updated_at) and let consumers fetch the rest.

GET /events?after=evt_1041&types=email.*JSON
{
  "data": [
    {
      "id": "evt_1042",
      "type": "email.delivered",
      "timestamp": "2026-10-02T19:12:04Z",
      "data": {
        "email_id": "em_31",
        "status": "delivered"
      }
    }
  ],
  "next_cursor": "evt_1042"
}
POKE.md

Is your service POKE?

Point your coding agent at POKE.md. It audits the codebase with file and line evidence for each tenet, ranks the endpoints consumers are likely polling, and proposes the smallest next steps. Ask it to “make it POKE” and it implements them.

Is my service POKE? Check svix.com/POKE.md
Why POKE

Agents are multiplying your API traffic

Agent traffic is overtaking human traffic, and agents use APIs very differently from the people those APIs were designed for.

1,700%+

Growth in daily AI agent requests on Cloudflare's network in a single year, from June 2025 to May 2026.

Source: Cloudflare
257M

Agent web requests on Mintlify-powered docs in August 2026, against 131M human page loads.

Source: Mintlify, State of Knowledge 2026

Why agents generate more activity

They iterate
Try, fail, adapt, try again. Every attempt is another request.
They explore
Agents run several paths in parallel, often through sub-agents, and every path hits your API.
They verify
Before acting, an agent re-reads the docs, the changelog and the API reference to make sure it isn't working from stale knowledge.
They go ad-hoc
There's no saved script. Every run starts from scratch and rediscovers what the last one already knew.
They lower the barrier
People ask agents for things they would never have done by hand, so work that never existed now generates traffic.
They monitor
“Check every minute” is one sentence to an agent, and it will check every resource, every minute, indefinitely.

To be useful, agents need even more. They have to react to things in real time, work from live context instead of stale data, and know when a slow operation has finished. Without events, the only way to get any of that is to poll.

Polling

Why polling breaks at agent scale

Polling is dead simple, the API is already there, and if you miss a change you catch it next time. That's why everyone reaches for it first (we compare the two approaches in webhooks vs. API polling). At agent scale it fails in three ways.

Problem 1: the load

A person might check something ten times a day. An agent checks every minute. Around 99.9% of those requests come back with nothing changed, and your servers pay for every one.

Problem 2: latency compounds

Agent workflows chain steps: start a job, poll until it's done, start the next. Six steps at about 15 seconds of polling each is 90 seconds of waiting, even when every operation is fast.

Polling faster brings back problem 1. Faster polling multiplies load, slower polling multiplies latency.

Problem 3: most APIs aren't polling friendly

List endpoints don't show deletions, so a consumer only notices one by diffing the whole state, and changes between polls can be missed entirely.

Lists sorted by time have a subtler trap. Say a consumer has read every entry up to 10:05. An entry stamped 10:04 that commits a second later lands behind its position, and no later poll will ever return it.

The other fix is to scale: when traffic grows 20x, grow your infrastructure 20x with load balancers, read replicas, shards and caches. It works, and you pay for it in cloud spend and complexity.

Event-driven

Why not classic event-driven architecture or plain webhooks?

Classic event-driven architecture is heavyweight

Event-driven architecture is the right idea: publish a message whenever state changes in a meaningful way, and let loosely coupled consumers react. The term was coined in 2003, and it never became the default because it was built and sold for enterprises, as an all-or-nothing architecture with heavyweight event brokers and ceremony.

It solved a problem most teams didn't have yet. Agents just gave everyone that problem.

Webhooks alone leave gaps

Webhooks are boring, battle-tested and everywhere: Stripe, Shopify and GitHub all send them. On their own they leave gaps. Ordering and duplicates are the consumer's problem, they need a public always-on endpoint, there's no way to bootstrap existing state, they're hard to debug and test, and subscribing is usually manual.

POKE keeps the parts that work. The same way REST is the simple-is-better approach to HTTP APIs, POKE is the simple-is-better approach to event-driven architecture.

Supabase Select 2026

Watch the talk: Agents are going to kill your service

Tom Hacohen introduced POKE architecture at Supabase Select 2026. In about 12 minutes he covers why agent traffic breaks polling, why event-driven architecture never caught on, and how to make your backend POKE.

Tom HacohenFounder and CEO of Svix, co-creator of Standard Webhooks

Don't want to build the push side?

Svix delivers Standard Webhooks with retries, signatures and an embeddable customer portal. Feed it from your event log and skip building delivery.

What is POKE architecture?

POKE architecture (Publish Once, Kickoff Everywhere) is a lightweight event-driven design for APIs. Every change a consumer cares about is published once to an append-only event log per customer. Agents and integrations read that log from a cursor or get it pushed as webhooks, instead of polling your API.

What does POKE stand for?

Publish Once, Kickoff Everywhere. Each event is published once, to the customer's append-only log, and every consumer (pull clients, webhook endpoints and agents) kicks off from that same log.

How is POKE different from event-driven architecture?

POKE is a deliberately small form of event-driven architecture for services that expose events to their customers. Instead of a broker and an all-or-nothing migration, it needs one append-only log per customer, plain HTTP for pulling and Standard Webhooks for pushing, and it can be added next to an existing API one resource at a time.

Does POKE replace webhooks?

No. Webhooks are POKE's push side. The difference is that they're delivered from the same log the pull API reads, so a consumer that missed a delivery or was offline catches up from its cursor.

Do I need Kafka or a message broker?

No. A Postgres table is enough to start, as long as readers can't skip events under concurrent writes. If you already run Kafka, partition by customer ID and materialize each customer's events into a store with a per-customer cursor.

Is POKE a Svix product?

No. POKE is an open architecture pattern, and POKE.md is free for any agent to use. Svix can run the webhook delivery side if you'd rather not build it.