Skip to main content
This page documents every event type Recurr emits. All events share the common envelope documented in Overview; each event’s data field is documented per type below. Event types follow the convention <resource>.<action>:
  • subscription.* — subscription lifecycle
  • payment.* — individual charge events
  • motion.* — lifecycle motion conversions
  • ticket.* — subscriber-submitted tickets

Subscription events

subscription.activated

A subscription becomes active for the first time. Triggers:
  • New web sub from direct-web acquisition
  • Existing app-store sub successfully migrated to web
data fields:
FieldTypeDescription
sourcestring"direct_web" or "migration"
cohort_idstring | nullCohort ID if the activation came via a Recurr-orchestrated motion
attribution_sourcestring | nullAcquisition channel for direct_web (e.g., "google_ads", "tiktok_ads", "organic", "email")
first_payment_amountintegerActivation payment amount in smallest currency unit
first_payment_currencystringISO 4217 currency code
trial_days_remaininginteger | nullDays remaining in trial if activated under a trial; null if direct paid activation
Example:
{
  "id": "evt_01HQX8K9M1P0R5N3Y2T7B4C6V",
  "type": "subscription.activated",
  "schema_version": "v1",
  "created_at": "2026-05-22T12:34:56Z",
  "tenant": { "id": "tnt_app123", "name": "ExampleApp" },
  "subscriber": { "id": "subscriber_01HQ...", "email": "user@example.com", "email_hashed": "sha256:abc...", "created_at": "2025-03-10T00:00:00Z" },
  "subscription": { "id": "sub_01HQ...", "status": "active", "plan": "premium_monthly", "current_period_start": "2026-05-22T00:00:00Z", "current_period_end": "2026-06-22T00:00:00Z" },
  "data": {
    "source": "migration",
    "cohort_id": "cohort_q3_pilot",
    "attribution_source": null,
    "first_payment_amount": 999,
    "first_payment_currency": "USD",
    "trial_days_remaining": null
  }
}

subscription.renewed

A subscription renews — charge succeeded on an existing subscription. data fields:
FieldTypeDescription
renewal_countintegerNumber of renewals to date (excludes initial activation)
payment_amountintegerRenewal amount in smallest currency unit
payment_currencystringISO 4217 currency code
next_renewal_atstringISO 8601 timestamp of expected next renewal

subscription.upgraded

A subscription changes to a higher-priced plan or longer billing interval. Triggers:
  • Annual nudge motion converts (monthly → annual)
  • Plan switch within the billing portal (Pro → Premium)
  • Cancel save flow accepts a “switch plan” offer
data fields:
FieldTypeDescription
from_planstringPrevious plan identifier
to_planstringNew plan identifier
triggerstring"annual_nudge", "manual_switch", "cancel_save", "plan_change"
proration_amountintegerProration credit/charge applied in smallest currency unit (signed; negative = credit)
new_period_endstringISO 8601 timestamp of new period end

subscription.cancelled

A subscription enters cancelled state (immediate or scheduled-at-period-end). data fields:
FieldTypeDescription
cancel_atstringWhen the subscription effectively ends (ISO 8601)
at_period_endbooleantrue if cancelling at current period end; false if immediate
cancel_reasonstring | nullReason from exit survey ("too_expensive", "not_using", "found_alternative", "missing_feature", "life_change", "other")
cancel_reason_freetextstring | nullFree-text response from exit survey
tenure_daysintegerSubscriber’s tenure on Recurr (days since first activation)
Exit-survey fields (cancel_reason, cancel_reason_freetext) persist on the subscriber’s record and are available to subsequent winback motions for personalisation. This is the cleanest example of Recurr’s first-party data continuity — capture at cancel, use at winback months later.

subscription.recovered

A previously-failed or lapsed subscription returns to active state. Triggers:
  • Failed payment recovered via smart retry
  • Lapsed sub re-activated via winback motion
  • Manual reactivation by customer-facing ops
data fields:
FieldTypeDescription
recovery_methodstring"smart_retry", "winback_motion", "manual_reactivation"
days_since_lapseinteger | nullDays between lapse and recovery (null for smart_retry)
recovery_payment_amountintegerRecovery charge in smallest currency unit
recovery_payment_currencystringISO 4217 currency code
previous_cancel_reasonstring | nullOriginal cancel reason from exit survey (for winback context)

Payment events

Payment events fire per individual charge. Distinct from subscription.renewed — payment events cover both subscription renewals and non-subscription charges (gift purchases, one-time fees, motion performance fees deducted at settlement).

payment.succeeded

A charge succeeded. data fields:
FieldTypeDescription
amountintegerCharge amount in smallest currency unit
currencystringISO 4217 currency code
stripe_charge_idstringStripe charge ID for cross-reference
charge_typestring"subscription_renewal", "subscription_activation", "one_time", "motion_settlement"
motion_idstring | nullIf charge_type is motion_settlement, the motion ID

payment.failed

A charge attempt failed. data fields:
FieldTypeDescription
amountintegerAttempted amount in smallest currency unit
currencystringISO 4217 currency code
failure_reasonstringStripe failure code (e.g., "card_declined", "insufficient_funds", "expired_card")
retry_scheduled_atstring | nullISO 8601 timestamp of next smart-retry attempt
attempt_numberintegerWhich attempt this was (1 = initial, 2+ = retries)

payment.refunded

A refund issued against a prior charge. data fields:
FieldTypeDescription
refund_amountintegerRefund amount in smallest currency unit
refund_currencystringISO 4217 currency code
refund_reasonstring | null"customer_request", "goodwill", "chargeback", "duplicate", "fraudulent", "other"
is_partialbooleantrue if partial refund; false if full
original_charge_idstringStripe charge ID of the original payment

Motion events

Motion events fire when a lifecycle motion successfully converts a subscriber. These are the events Recurr earns performance fees against.

motion.cancel_save

A subscriber clicked Cancel, saw the save flow, and accepted a save offer. data fields:
FieldTypeDescription
save_offer_typestring"pause", "downgrade", "credit", "extend_trial", "sweetener"
original_cancel_reasonstring | nullReason captured by exit survey before the save offer was presented
save_offer_valueintegerValue of the save offer in smallest currency unit (e.g., credit amount, discount value)
annualised_revenueintegerSaved sub’s annualised revenue for performance-fee calculation

motion.winback_recovered

A previously-cancelled sub reactivated via winback motion. data fields:
FieldTypeDescription
cancelled_atstringWhen the sub originally cancelled (ISO 8601)
days_since_cancellationintegerDays between cancellation and winback recovery
winback_offerstring | nullWhat converted ("new_trial", "discount", "feature_announcement", "personalised_message")
original_cancel_reasonstring | nullOriginal cancel reason — used to personalise the winback approach
annualised_revenueintegerRecovered sub’s annualised revenue for performance-fee calculation

motion.annual_upgrade_converted

A monthly sub upgraded to annual via the annual nudge motion. data fields:
FieldTypeDescription
from_monthly_paymentintegerPrevious monthly payment in smallest currency unit
to_annual_paymentintegerNew annual payment in smallest currency unit
effective_discount_pctnumberEffective discount from monthly × 12 to annual price (0 if priced flat)
trigger_signalstringWhat triggered the nudge ("engagement_threshold", "renewal_proximity", "tenure_milestone", "manual")
annualised_revenueintegerEqual to to_annual_payment for this motion

Ticket events

ticket.submitted

A subscriber submitted a ticket via Recurr’s help center or billing portal. data fields:
FieldTypeDescription
topicstringPre-classified topic — "billing_question", "refund_request", "plan_change", "cancel", "payment_issue", "other"
subjectstringSubscriber’s subject line
bodystringSubscriber’s message body
sourcestringWhere the ticket originated — "help_center", "billing_portal", "email_reply"
prioritystring"normal", "high" (auto-elevated for refund requests, payment issues)
Pre-classification of topic uses Recurr’s billing context (which portal page the ticket originated from, the subscriber’s recent payment history, etc.). This enriched payload lets your CS tool’s AI triage make better routing decisions than a generic ticket capture.