Events & delivery
Every event Rustle delivers shares one store-agnostic envelope. The store of origin is a
field (store), never something your code has to branch on. A review from Apple and a
review from Google arrive in exactly the same shape.
The envelope
These fields are present on every event, regardless of type:
| Field | Type | Notes |
|---|---|---|
event_id | string | Identity of this delivery to this subscriber. Deterministic (not random). Dedupe on it. Also sent as the x-radar-event-id header. |
occurrence_id | string | Identity of the underlying occurrence, shared across every subscriber it fans out to. Lets you recognise “the same review, delivered to me.” |
event_type | enum | Which event this is; see the event catalog below. A field to branch on, never a code branch you maintain per store. |
store | enum | apple or google. A field, not a code branch. |
app_id | string | Store-native app id (Apple numeric id, Google package name). |
subscriber_id | string | The hook this was delivered to (your hook id). |
occurred_at | RFC 3339 | When the change happened at the source. |
observed_at | RFC 3339 | When Rustle detected it (poll time). |
schema_version | integer | Bumped only on a breaking payload change; currently 2. |
enrichment | object | null | Optional extra context on a review (sentiment, themes, a short summary); null otherwise. See review.created. |
The type-specific fields (review_id, rating, body, … for reviews;
current_rating, delta, … for rating drops; feature_slug, count, geo, … for
patterns) sit at the top level alongside the envelope. The payload is flattened, not
nested. The one exception is the optional enrichment object above, which is nested (or
null). See the event catalog below for every type and its reference page.
Event catalog
Rustle emits two kinds of event_type:
- Subscribable heads: the event types you attach a hook to. A hook’s
event_typeis always one of these. - Emit-only lifecycle riders: pattern events you never subscribe to directly. When you
subscribe to a pattern head (
feature.demand_surged/issue.surging) you receive its whole lifecycle; the riders arrive to the same hook, branded onevent_type.
Subscribable heads
event_type | What it is |
|---|---|
review.created | A newly-seen review passing your star filter (carries the free enrichment block). |
rating.dropped | The aggregate rating fell by at least your configured delta (sharp step). |
rating.trend | A gradual, smoothed decline in the aggregate rating (the gentle companion to rating.dropped). |
feature.demand_surged | Requests for a feature surged past their baseline: head of the demand-surge lifecycle. |
issue.surging | A problem theme surged past its baseline: head of the issue-surge lifecycle. |
Emit-only lifecycle riders
Delivered to subscribers of the family head above; not separately subscribable.
| Family | Riders |
|---|---|
feature.demand_surged | feature.demand_recurred, feature.demand_intensified, feature.demand_sustained, feature.demand_subsided |
issue.surging | issue.recurred, issue.intensified, issue.sustained, issue.subsided, issue.version_regressed |
Paid and free events
Enrichment and reviews are free; the pattern family is paid (the Rustle Indie tier).
- Free: every account receives
review.created(with itsenrichmentblock, the free hook) andrating.dropped. - Paid: the whole pattern family,
rating.trend,feature.demand_surgedandissue.surging, together with their emit-only lifecycle riders.
The gate is enforced at delivery: a pattern event bound for a non-entitled (free) hook is
silently skipped: never retried, never dropped noisily. review.created (enrichment
included) and rating.dropped are never gated.
Delivery
Rustle POSTs the event as a JSON body to your hook’s target_url, with two headers:
| Header | Purpose |
|---|---|
x-radar-event-id | The event’s event_id; dedupe on it. |
x-radar-signature | sha256=<hex> HMAC of the exact body; verify it. |
A 2xx response means you accepted the event. Any other response (or a timeout) is treated
as a failure and retried with backoff; events that exhaust their retries land in a
dead-letter queue rather than being dropped.
Forward-looking by default
A new hook only receives events that occur at or after it was created. It does not replay the back-catalogue already sitting in a store’s feed. (The same is true for rating drops: the first observation seeds the baseline silently.)