subscription.sync
Reconciles a single OrganizationSubscription against a Stripe payload and dispatches downstream workflows when the diff implies a richer business event (cancellation, dunning, plan change, recovery).
Reconcile one OrganizationSubscription against a Stripe payload.
Overview
| Property | Value |
|---|---|
| Workflow type | Linear |
| Library | App-subscription |
| Version | 1.0 |
Triggers
| Source | Endpoint / Event | Description |
|---|---|---|
| WEBHOOK | customer.subscription.updated | Status / plan / cancel_at changes |
| WEBHOOK | customer.subscription.deleted | Stripe-side termination |
| WEBHOOK | invoice.payment_failed | Triggers dunning dispatch |
Input Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organization_uuid | uuid | Yes | — | Owning organization UUID |
subscription_uuid | uuid | Yes | — | OrganizationSubscription UUID |
stripe_event_data | json | Yes | — | Stripe Subscription object (or compatible projection) |
source | string | No | — | webhook |
event_type | string | No | — | Stripe event.type when source=webhook |
trace_ref | string | No | — | Trace reference for request correlation |
Output Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organization_uuid | uuid | No | — | — |
subscription_uuid | uuid | No | — | — |
stripe_event_data | json | No | — | — |
source | string | No | — | — |
event_type | string | No | — | — |
trace_ref | string | No | — | — |
started_at | string | No | — | — |
loaded_at | string | No | — | — |
db_snapshot | json | No | — | — |
stripe_snapshot | json | No | — | — |
is_noop | boolean | No | — | True when DB and Stripe already agreed; nothing was written |
applied_changes | json | No | — | Map of column → new value written to OrganizationSubscription |
applied_at | string | No | — | — |
dispatched | json | No | — | List of {workflow_type, workflow_uuid} entries spawned downstream |
dispatched_at | string | No | — | — |
paid_rbac_release | json | No | — | Paid RBAC actor release gate for an ended platform subscription (canceled / unpaid / incomplete_expired): decision, and the subscription.actor-rbac-seat.resize release run when owed |
diff | json | No | — | Full diff payload: changes map + derived flags (plan_changed, cancellation_scheduled, entered_dunning, recovered, terminated) |
stripe_subscription_ref | string | No | — | Stripe subscription reference seen on this run |
completed_at | string | No | — | — |
failed_at | string | No | — | — |
failure_reason | string | No | — | — |
failure_type | string | No | — | — |
failed_action | string | No | — | — |
failed_at_state | string | No | — | — |
failed_step | string | No | — | — |
failed_layer | string | No | — | — |
error | string | No | — | — |
error_type | string | No | — | — |
States
| State | Initial | Terminal | Success | Auto-advance | Description |
|---|---|---|---|---|---|
initiated | Yes | No | — | load_current | Sync request received |
applying | No | No | — | dispatch | Writing the merged state to OrganizationSubscription |
diffing | No | No | — | apply | Computing diff between DB and Stripe |
dispatching | No | No | — | complete | Conditionally spawning downstream workflows |
loading | No | No | — | compute_diff | Loading current DB row |
completed | No | Yes | Yes | — | Sync completed |
failed | No | Yes | No | — | Sync failed |
State Diagram
Transitions
| From | Action | To | Description |
|---|---|---|---|
initiated | load_current | loading | Load DB row |
loading | compute_diff | diffing | Compute diff |
diffing | apply | applying | Apply diff to DB (no-op when is_noop) |
applying | dispatch | dispatching | Dispatch downstream |
dispatching | complete | completed | Mark complete |
* (any state) | fail | failed | Catch-all failure handler |
Outcomes
| Outcome | Type | Description | State Data Keys |
|---|---|---|---|
completed | SUCCESS | Sync run terminated. state_data.is_noop=True means DB and Stripe already agreed; otherwise applied_changes and dispatched record what was done. | is_noop, applied_changes, dispatched, completed_at |
failed | FAILURE | Sync failed — see failure_reason | failed_at, failure_reason |
Business Errors
| Code | Message Template |
|---|---|
SUBSCRIPTION_NOT_FOUND | OrganizationSubscription {subscription_uuid} not found |
STRIPE_PAYLOAD_INVALID | Stripe event payload is empty or missing required fields |
Alarms
| Name | Type | State | Severity | Description |
|---|---|---|---|---|
sync_failed | state_entered | failed | warning | subscription.sync run reached FAILED |
API Usage
bash
# Step 1: Start the workflow
POST /api/workflows/start
Content-Type: application/json
{
"workflow_type": "subscription.sync",
"initial_data": {
"organization_uuid": "value",
"subscription_uuid": "value",
"stripe_event_data": "value"
}
}
# Step 2: Webhook triggers transition (customer.subscription.updated)
POST /api/workflows/{workflow_id}/transition
Content-Type: application/json
{ "action": "...", ... webhook payload ... }