Get started

Where the rules run

How the Server SDK rules on entitlements inside your own process, how plan changes reach it, and what keeps working while your application's region is unreachable.

ShipRev does not sit in the path of your requests. Your backend verifies the caller and rules on entitlements inside its own process, from a copy of your buyer organizations' entitlements that the Server SDK keeps in memory. The network carries the changes to that copy and the usage you report; an ordinary ruling makes no network call.

Three parties, and who talks to whom

text
 End user's browser
   │  sends ShipRev requests to your origin
   ▼
 Your backend (Server SDK)
   verifyToken()    who is calling        local
   adjudicate()     may they do it        local, from the mirror
   reportUsage()    what they consumed    buffered, sent in batches
   │
   ▼
 Your application's region
   pushes changes to the mirror, receives usage
  • With the React components, the browser talks to your origin. The React components send their requests to a path on your own origin, which your server forwards to your brand domain; see The React components. They hold no secret and make no decision. Hosted sign-in pages and the customer portal are served on your brand domain, and checkout and the billing portal run on Stripe's pages.
  • Your backend decides. Whether a request may proceed is ruled by the Server SDK in your backend. What the frontend shows, such as a usage meter, is display only.
  • The region keeps your backend current. It sends your backend the entitlements of your buyer organizations and every change to them, and it receives and counts your usage.

Your region also has a link of its own to ShipRev. That second link, and what happens when it is interrupted, is covered in If the link between your region and ShipRev is interrupted. This page is about the link between your backend and your region.

The snapshot and the mirror

For each buyer organization, the region keeps an entitlement snapshot: for every capability its plan grants, the allowance of the current period, what is used, and the overage policy. When the Server SDK starts, it loads the snapshots of all buyer organizations of your application into memory. That in-memory copy is the mirror, and snapshot_revision in the client's state is its revision.

adjudicate() rules from the mirror. It is synchronous and does no I/O, so a ruling costs no network round trip. adjudicateAsync() does the same, but when the mirror does not know the organization or the capability and the region is reachable, it asks the region instead. The ruling's source says which happened:

sourceWhere the ruling came from
realtimeThe mirror, while the client is online
authoritativeThe region, asked by adjudicateAsync() because the mirror did not know the organization or capability
degradedThe last snapshot kept in the mirror, while the push stream is down
client_defaultNo basis for a ruling: an invalid request, a cold start, or an organization or capability the mirror does not know

Session tokens are verified locally as well. Once the client has reached the region once, it holds the region's signing keys and the identity of your application, and verifyToken() keeps working offline.

How changes reach your process

From start() to stop(), the client holds a push stream open to the region. When a buyer organization's entitlements change, for example because it moved to another plan, the region announces it on the stream and the client refreshes that organization at once. The next ruling uses the new entitlements, without polling and without a restart of your process.

Changing what you sell works the same way. Publishing a new plan version does not move anyone: existing subscribers stay on the version they subscribed to. When a buyer organization's subscription changes to the new version, its entitlements change, and the change is pushed.

The push stream is the client's only signal that it is online. The client renews its service token before it expires and reconnects with backoff; back online, it reloads the whole snapshot.

While the region is unreachable

Every ruling names an operation, read or write, because the two behave oppositely offline. Reads keep working from the last snapshot; writes stop.

Client stateWhenreadwrite
LIVEOnline, mirror loadedRuled from the mirrorRuled from the mirror
DISCONNECTEDPush stream down, last snapshot keptAllowed from the last snapshotRefused: disconnected_write
COLD_OFFLINEOffline, no snapshot loadedDecided by presets.cold_start, deny by defaultRefused: disconnected_write

Anything that consumes quota, changes state or costs money is a write; a check that only needs the capability granted can be a read with quantity: 0.

Usage reported while the region is unreachable waits in a backlog and is delivered, ahead of new events, once the client is back online. Where the backlog lives is your choice: in memory, the default, it does not survive a restart of your process; a local journal or your own store does. See Subscriptions reach you without polling.

How usage gets back to the region

reportUsage() does not send anything on its own. It adds the event to an in-memory buffer, which the client sends in batches: every 100 events, every 5 seconds, on flush() and on stop(). Each event carries your own usage_event_id, and the region drops repeats, so an event sent twice is counted once. Events the region refuses are passed to your on_rejected callback; see Refused usage events.

The region counts accepted events against the buyer organization's allowance. Rulings then show what is left as remaining, and the allowance of the period as limit.

What to watch in production

Expose the client's state() in your health endpoint. It returns the state, snapshot_revision and last_sync_at, the time of the mirror's last update; LIVE means all is well. The Server SDK reference lists every state.

© 2026 ShipRev