{
  "openapi": "3.0.3",
  "info": {
    "title": "NoChurn API",
    "version": "v1",
    "description": "Recover failed payments, prevent cancellations and win back churned customers.\nNoChurn runs in the EU: all data is hosted in the EU.\n\nAuthentication: `Authorization: Bearer nc_live_…` (or `nc_test_…` for sandbox).\nThe dashboard uses a session cookie instead (see the Auth endpoints).\n\nErrors always use the Error schema: `{ error: { code, message, fix, docs_url } }`.\nCustomers can be referenced by their NoChurn id (`cst_…`), their Stripe id (`cus_…`)\nor your own id (`external_id`), see GET /v1/customers/resolve.\n\nNoChurn's own client code (the widget, the MCP server, the SDKs) sends `NoChurn-Client: <name>/<semver>`\n(e.g. `widget/1.0.0`, `mcp/0.1.0`, `node/1.2.0`; name `^[a-z][a-z0-9-]{1,31}$`, strict semver, 32 characters\nat most) so that GET /v1/account/clients can tell you when to update it. Optional: your own code sends nothing.\n"
  },
  "servers": [
    {
      "url": "https://api.nochurn.sh",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Customers",
      "description": "The people and companies who pay you: their three ids (cst_, cus_, your external_id), churn score, access and custom attributes."
    },
    {
      "name": "Billing",
      "description": "Subscriptions and payments sent by your own billing, when you don't use a connected billing tool."
    },
    {
      "name": "Subscriptions",
      "description": "Subscriptions of a customer, from the billing source."
    },
    {
      "name": "Invoices",
      "description": "Invoices of a customer, from the billing source."
    },
    {
      "name": "Payment methods",
      "description": "Payment methods of a customer: brand, last4, expiry and type only, never card numbers."
    },
    {
      "name": "Events",
      "description": "Every decision NoChurn takes, with its reason (nevt_…)."
    },
    {
      "name": "Flows",
      "description": "Recovery, prevention, cancel and win-back flows: drafts, publishing, emails and runs."
    },
    {
      "name": "Cancel flow",
      "description": "The cancel flow: its configuration, design, sessions and the customer's answers."
    },
    {
      "name": "Messages",
      "description": "Emails NoChurn sends: the review queue, customers' replies and addresses never emailed again."
    },
    {
      "name": "Chargebacks",
      "description": "Disputes and early fraud warnings: evidence, replies and refunds."
    },
    {
      "name": "Data & AI",
      "description": "Custom data, churn drivers, campaigns, queries and feedback."
    },
    {
      "name": "Integrations",
      "description": "Billing tools, payment providers, app stores and help desks connected to NoChurn."
    },
    {
      "name": "Stripe",
      "description": "Stripe Connect: the connection, the import and its settings."
    },
    {
      "name": "Email domain",
      "description": "Your sending domain: sender, DNS records and verification."
    },
    {
      "name": "Developers",
      "description": "Webhook endpoints, their deliveries, and the versions of NoChurn's client code you run."
    },
    {
      "name": "API keys",
      "description": "Secret keys (nc_…) and publishable keys (pk_…), full or restricted."
    },
    {
      "name": "Logs",
      "description": "The unified log: API requests, webhooks and decisions."
    },
    {
      "name": "Account",
      "description": "Account settings, setup progress and sending schedule."
    },
    {
      "name": "Team",
      "description": "Members, roles and invitations."
    },
    {
      "name": "AI apps",
      "description": "AI apps connected over MCP (OAuth), and live requests waiting for a human."
    },
    {
      "name": "Agent integration",
      "description": "What coding agents use to add NoChurn to an app: platform detection, tested snippets and the checklist."
    },
    {
      "name": "Sandbox",
      "description": "Test-mode scenarios: create a customer, fail a payment, remove a card, run a cancel flow."
    },
    {
      "name": "Widget",
      "description": "Calls made by the browser script (js.nochurn.sh) with a publishable key."
    },
    {
      "name": "In-app",
      "description": "The in-app banner and wall of one customer, for the browser script."
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Secret API key (nc_live_… or nc_test_…), or an AI app's OAuth access token (ncat_…, test mode by default, docs/MCP.md)"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message",
              "fix",
              "docs_url"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "invalid_request",
                  "conflict",
                  "internal_error",
                  "rate_limited",
                  "customer_not_found",
                  "ambiguous_customer",
                  "stripe_not_configured",
                  "stripe_error",
                  "two_factor_required",
                  "attribute_type_mismatch",
                  "attribute_limit_reached",
                  "provider_not_available",
                  "capability_missing",
                  "dispute_already_submitted",
                  "dispute_not_supported_by_provider",
                  "provider_error",
                  "evidence_file_unsupported",
                  "evidence_file_too_large",
                  "evidence_files_limit",
                  "logo_unsupported",
                  "logo_too_large",
                  "plan_billing_not_configured",
                  "custom_plan_required",
                  "test_mode_only",
                  "live_approval_required",
                  "live_read_not_granted",
                  "insufficient_scope",
                  "publishable_key_required",
                  "payload_too_large",
                  "subscription_not_found",
                  "subscription_required",
                  "email_domain_provisioning_failed",
                  "insufficient_data",
                  "nothing_to_resume"
                ]
              },
              "message": {
                "type": "string"
              },
              "fix": {
                "type": "string"
              },
              "docs_url": {
                "type": "string",
                "format": "uri"
              },
              "retry_after": {
                "type": "integer",
                "description": "Seconds to wait (rate_limited only)."
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "Account": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "name",
          "billing_source",
          "sandbox",
          "default_locale",
          "plan",
          "external_id_metadata_key",
          "require_two_factor",
          "timezone",
          "audience",
          "reporting_currency",
          "onboarding",
          "disputes_settings",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "account"
            ]
          },
          "id": {
            "type": "string",
            "example": "acc_7Zf3kQ2mV9xYpL4a"
          },
          "name": {
            "type": "string"
          },
          "billing_source": {
            "type": "string",
            "enum": [
              "stripe",
              "api"
            ]
          },
          "sandbox": {
            "type": "boolean"
          },
          "default_locale": {
            "type": "string",
            "enum": [
              "en",
              "de",
              "fr",
              "it",
              "pt"
            ]
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "paid",
              "custom"
            ],
            "description": "NoChurn's own plan: details in GET /v1/billing/plan."
          },
          "external_id_metadata_key": {
            "type": "string",
            "description": "Stripe customer metadata key copied into external_id. Default: user_id."
          },
          "require_two_factor": {
            "type": "boolean",
            "description": "Every member must use two-factor authentication. Owners only, from a 2FA session."
          },
          "timezone": {
            "type": "string",
            "example": "Europe/Paris",
            "description": "IANA time zone: \"this month\" and \"today\" in the dashboard."
          },
          "audience": {
            "type": "string",
            "nullable": true,
            "enum": [
              "b2b",
              "b2c",
              null
            ],
            "description": "Who your customers are: sets the default send window (GET /v1/account/send_schedule)."
          },
          "reporting_currency": {
            "type": "string",
            "nullable": true,
            "example": "eur",
            "description": "Currency the dashboard converts every amount into (ECB rates). null: the connected Stripe account's default currency, else the one most customers pay in."
          },
          "onboarding": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "step",
              "skipped",
              "completed_at"
            ],
            "properties": {
              "step": {
                "type": "string",
                "enum": [
                  "billing",
                  "support",
                  "cancel_modal",
                  "email_domain",
                  "done"
                ],
                "description": "Where the onboarding resumes."
              },
              "skipped": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "support",
                    "cancel_modal",
                    "email_domain"
                  ]
                },
                "description": "Set up later."
              },
              "completed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false
          },
          "disputes_settings": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "auto_submit",
              "auto_submit_hours_before",
              "auto_refund_fraud_warnings",
              "auto_refund_max_cents",
              "cancel_subscription_on_fraud_refund",
              "notify_email",
              "notify_slack",
              "contest_cost_cents",
              "contest_cost_currency",
              "auto_submit_min_score"
            ],
            "properties": {
              "auto_submit": {
                "type": "boolean",
                "description": "Off by default: a human approves and sends."
              },
              "auto_submit_hours_before": {
                "type": "integer",
                "minimum": 1,
                "maximum": 168
              },
              "auto_refund_fraud_warnings": {
                "type": "boolean",
                "description": "Off by default."
              },
              "auto_refund_max_cents": {
                "type": "integer",
                "nullable": true
              },
              "cancel_subscription_on_fraud_refund": {
                "type": "boolean"
              },
              "notify_email": {
                "type": "boolean"
              },
              "notify_slack": {
                "type": "boolean",
                "description": "Post to the Slack channel of Integrations (if connected)."
              },
              "contest_cost_cents": {
                "type": "integer",
                "nullable": true,
                "description": "What contesting costs you (provider fee and time). Unset: no suggestion to accept."
              },
              "contest_cost_currency": {
                "type": "string",
                "nullable": true,
                "description": "Currency of contest_cost_cents; null = the reporting currency."
              },
              "auto_submit_min_score": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Automatic submission only from this chance of winning (70 by default)."
              }
            },
            "nullable": false
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "ApiKey": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "name",
          "kind",
          "mode",
          "access",
          "permissions",
          "last4",
          "allowed_origins",
          "token",
          "first_used_at",
          "last_used_at",
          "revoked_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "api_key"
            ]
          },
          "id": {
            "type": "string",
            "example": "key_3Srt9txFqHsFTSjb"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "secret",
              "publishable"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "access": {
            "type": "string",
            "enum": [
              "full",
              "restricted"
            ],
            "description": "full: every resource. restricted: see permissions."
          },
          "permissions": {
            "type": "object",
            "nullable": true,
            "additionalProperties": false,
            "description": "Level per resource (none, read, write). null for publishable keys.",
            "properties": {
              "account": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "api_keys": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "team": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "events": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "customers": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "billing": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "stripe_connections": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "logs": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "flows": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "email_domains": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "messages": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "webhooks": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "cancel_sessions": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "reviews": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "dashboard": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "insights": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "integrations": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "queries": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "disputes": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              },
              "sandbox": {
                "type": "string",
                "enum": [
                  "none",
                  "read",
                  "write"
                ]
              }
            }
          },
          "last4": {
            "type": "string"
          },
          "allowed_origins": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Publishable keys: origins allowed to call the public endpoints (null on secret keys)."
          },
          "token": {
            "type": "string",
            "nullable": true,
            "description": "Secret keys: only on creation, store it now. Publishable keys: always."
          },
          "first_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "First call made with this key (onboarding: waiting for your first event)."
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Event": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "type",
          "subject",
          "reason",
          "actor",
          "livemode",
          "data",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "event"
            ]
          },
          "id": {
            "type": "string",
            "example": "nevt_tZCYqtEDVAvwZjD5"
          },
          "type": {
            "type": "string",
            "example": "api_key.created"
          },
          "subject": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "id"
            ],
            "properties": {
              "type": {
                "type": "string"
              },
              "id": {
                "type": "string"
              }
            },
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "actor": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "id",
              "name"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "api_key",
                  "user",
                  "system",
                  "ai",
                  "ai_agent",
                  "customer"
                ]
              },
              "id": {
                "type": "string",
                "nullable": true,
                "description": "ai_agent: the OAuth grant (ogr_…) of the AI app."
              },
              "name": {
                "type": "string",
                "nullable": true,
                "description": "ai_agent: the MCP client's name (as it registered)."
              }
            },
            "nullable": false
          },
          "livemode": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "StripeConnection": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "stripe_account_id",
          "livemode",
          "status",
          "smart_retries_mode",
          "internal_id",
          "backfill",
          "history",
          "last_webhook_at",
          "last_webhook_type",
          "disconnected_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "stripe_connection"
            ]
          },
          "id": {
            "type": "string",
            "example": "scn_8kQ2mV9xYpL4aZf3"
          },
          "stripe_account_id": {
            "type": "string",
            "example": "acct_1Nv0FGQ9RKHgCVdK"
          },
          "livemode": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "error"
            ]
          },
          "smart_retries_mode": {
            "type": "string",
            "enum": [
              "stripe",
              "nochurn"
            ],
            "description": "stripe: keep Stripe Smart Retries. nochurn: NoChurn schedules the retries (turn Smart Retries off in Stripe)."
          },
          "internal_id": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "field",
              "defaults",
              "fields"
            ],
            "properties": {
              "field": {
                "type": "string",
                "nullable": true,
                "description": "The field this connection names (PATCH internal_id_field); null = defaults.",
                "example": "custom_fields.user_id"
              },
              "defaults": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "custom_fields.user_id",
                  "code"
                ],
                "description": "Read in this order when field is null; user_id is the account's external_id_metadata_key."
              },
              "fields": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "code",
                  "custom_fields.<name>"
                ],
                "description": "The shapes a field may take for this tool (<name> = one key)."
              }
            },
            "nullable": true,
            "description": "Where NoChurn reads your app's id for a customer (your user id, stored as external_id) in this tool; null for a tool that carries none (stores, universal webhook). Nothing found: the email decides."
          },
          "backfill": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "progress",
              "error",
              "started_at",
              "finished_at"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "running",
                  "done",
                  "failed"
                ]
              },
              "progress": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                },
                "description": "Records imported so far: customers, subscriptions, invoices, payment_methods."
              },
              "error": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "message",
                  "fix",
                  "docs_url"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "enum": [
                      "import_access_denied",
                      "import_interrupted",
                      "import_failed"
                    ]
                  },
                  "message": {
                    "type": "string"
                  },
                  "fix": {
                    "type": "string"
                  },
                  "docs_url": {
                    "type": "string"
                  }
                },
                "nullable": true,
                "description": "Why the import stopped and what to do; null unless it failed."
              },
              "started_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false
          },
          "history": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "since",
              "progress",
              "error",
              "started_at",
              "finished_at"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "running",
                  "done",
                  "failed"
                ]
              },
              "since": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "progress": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                },
                "description": "Records imported so far: invoices, charges."
              },
              "error": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "message",
                  "fix",
                  "docs_url"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "enum": [
                      "import_access_denied",
                      "import_interrupted",
                      "import_failed"
                    ]
                  },
                  "message": {
                    "type": "string"
                  },
                  "fix": {
                    "type": "string"
                  },
                  "docs_url": {
                    "type": "string"
                  }
                },
                "nullable": true,
                "description": "Why the import stopped and what to do; null unless it failed."
              },
              "started_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false,
            "description": "12-month history import (invoices and payment attempts), after the backfill."
          },
          "last_webhook_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_webhook_type": {
            "type": "string",
            "nullable": true,
            "example": "invoice.payment_failed",
            "description": "Type of the newest Stripe event received for this connection; null before the first."
          },
          "disconnected_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Customer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "stripe_customer_id",
          "external_id",
          "stripe_account",
          "livemode",
          "email",
          "company_domain",
          "name",
          "locale",
          "country",
          "region",
          "timezone",
          "timezone_source",
          "attributes",
          "churn_score",
          "churn_level",
          "flag",
          "state",
          "plan",
          "mrr_cents",
          "lifetime_value_cents",
          "currency",
          "payment_method",
          "last_seen_at",
          "churned_at",
          "flows_paused_at",
          "signup_at",
          "created_at",
          "updated_at",
          "identifiers"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer"
            ]
          },
          "id": {
            "type": "string",
            "example": "cst_4fJ8sK2pQ9wLm3Xz",
            "description": "NoChurn id, always present."
          },
          "stripe_customer_id": {
            "type": "string",
            "nullable": true,
            "example": "cus_QwErTy123",
            "description": "Stripe customer id, when billed by Stripe."
          },
          "external_id": {
            "type": "string",
            "nullable": true,
            "example": "u_123",
            "description": "The id of this customer in your app."
          },
          "stripe_account": {
            "type": "string",
            "nullable": true,
            "example": "acct_1Nv0FGQ9RKHgCVdK"
          },
          "livemode": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "company_domain": {
            "type": "string",
            "nullable": true,
            "example": "acme.com",
            "description": "Domain of the email when it is a company's (not a free, disposable or placeholder address): the customer's logo is GET /v1/logos/{company_domain}."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "locale": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "region": {
            "type": "string",
            "nullable": true,
            "example": "TX",
            "description": "State or province of the address."
          },
          "timezone": {
            "type": "string",
            "nullable": true,
            "example": "America/Chicago",
            "description": "IANA time zone used to reach the customer."
          },
          "timezone_source": {
            "type": "string",
            "nullable": true,
            "enum": [
              "api",
              "attribute",
              "address",
              "country_guess",
              "ip_country",
              "card_country",
              "account",
              null
            ],
            "description": "api (sent by you, never overwritten), attribute, address, country_guess (main zone of a multi-zone country), ip_country, card_country, account (fallback)."
          },
          "attributes": {
            "type": "object",
            "additionalProperties": true,
            "description": "Custom attributes (M4/M6)."
          },
          "churn_score": {
            "type": "integer",
            "nullable": true
          },
          "churn_level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              null
            ],
            "nullable": true
          },
          "flag": {
            "$ref": "#/components/schemas/CustomerFlag"
          },
          "state": {
            "type": "string",
            "enum": [
              "none",
              "trialing",
              "active",
              "in_recovery",
              "paused",
              "churned"
            ],
            "description": "From billing: in_recovery = an open invoice with a failed attempt; churned = no live subscription."
          },
          "plan": {
            "type": "string",
            "nullable": true,
            "example": "Pro",
            "description": "Plan of the main live subscription."
          },
          "mrr_cents": {
            "type": "integer",
            "description": "Monthly recurring revenue, in the minor unit of currency."
          },
          "lifetime_value_cents": {
            "type": "integer",
            "description": "Paid invoices seen by NoChurn (12 months of Stripe history, then all)."
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "example": "eur",
            "description": "Billing currency. Amounts are never converted."
          },
          "payment_method": {
            "$ref": "#/components/schemas/PaymentMethodSummary"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "From the last_active_at attribute your app sends."
          },
          "churned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "flows_paused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Set by Pause all flows: no flow acts on this customer."
          },
          "signup_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "identifiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerIdentifier"
            },
            "description": "Every identifier a source gave for this customer, oldest first: how Stripe, your other tools, your app and support all reach this one customer (docs: customer matching)."
          },
          "risk": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "next_renewal_at",
              "cancel_at_period_end",
              "score_factors",
              "score_computed_at"
            ],
            "properties": {
              "next_renewal_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Soonest end of the current period of a paying subscription (active or past_due) still to come."
              },
              "cancel_at_period_end": {
                "type": "boolean",
                "nullable": true,
                "description": "That subscription stops then instead of renewing. null without a renewal."
              },
              "score_factors": {
                "type": "array",
                "maxItems": 5,
                "description": "Factors that raised the last churn score, largest first (starting point and lowering factors left out).",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "key",
                    "label",
                    "points"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "failed_payments"
                    },
                    "label": {
                      "type": "string",
                      "example": "2 failed payments in 90 days"
                    },
                    "points": {
                      "type": "integer"
                    }
                  },
                  "nullable": false
                }
              },
              "score_computed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false,
            "description": "With include[]=risk (list only): when the customer renews and why the score is what it is. Early signals (card removed…) are in flag."
          }
        },
        "nullable": false
      },
      "CustomerIdentifier": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "provider",
          "connection",
          "value",
          "source",
          "created_at"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "stripe",
              "external",
              "email",
              "provider",
              "nochurn"
            ],
            "description": "stripe (cus_), provider (a billing, store or support tool's id), external (your id, current or previous), email, nochurn (the cst_ id of a customer merged into this one, still accepted)."
          },
          "provider": {
            "type": "string",
            "nullable": true,
            "example": "chargebee",
            "description": "The tool for stripe and provider ids (stripe, chargebee, app_store, intercom…)."
          },
          "connection": {
            "type": "string",
            "nullable": true,
            "example": "bcn_2Kd9sL2mQx7VbN4p",
            "description": "The billing connection (scn_ / bcn_) or support integration (itg_) the id belongs to."
          },
          "value": {
            "type": "string",
            "example": "cus_QwErTy123"
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "stripe_metadata",
              "stripe",
              "manual",
              "provider",
              "support",
              "import",
              "merge"
            ],
            "description": "Who gave it: api, import, stripe, stripe_metadata, provider, support, manual, merge (moved by a merge)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "CustomerDuplicate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "customer",
          "other_customer",
          "people",
          "identifier",
          "reason",
          "status",
          "suggested_keep",
          "kept_customer",
          "dismissed_at",
          "merged_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_duplicate"
            ]
          },
          "id": {
            "type": "string",
            "example": "cdp_4fJ8sK2pQ9wLm3Xz"
          },
          "livemode": {
            "type": "boolean"
          },
          "customer": {
            "type": "string",
            "description": "The pair is stored with the smaller id first."
          },
          "other_customer": {
            "type": "string"
          },
          "people": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": {
              "$ref": "#/components/schemas/CustomerDuplicatePerson"
            },
            "description": "Name and email of customer then other_customer; null for one that no longer exists (merged away)."
          },
          "identifier": {
            "type": "string",
            "enum": [
              "stripe",
              "provider",
              "external",
              "email"
            ],
            "description": "What they share: an email, or ids one source sent together."
          },
          "reason": {
            "type": "string",
            "example": "share an email"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "dismissed",
              "merged"
            ],
            "description": "open (a To do), dismissed (not the same person), merged (one customer absorbed into the other)."
          },
          "suggested_keep": {
            "type": "string",
            "nullable": true,
            "example": "cst_4fJ8sK2pQ9wLm3Xz",
            "description": "Open: the customer to preselect as the one to keep: the one with a live subscription, else the one billed by a tool (Stripe or another), else the oldest. Merged: the kept one. Dismissed: null."
          },
          "kept_customer": {
            "type": "string",
            "nullable": true,
            "description": "Merged: the customer that holds everything now (the other one no longer exists)."
          },
          "dismissed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "merged_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "CustomerDuplicatePerson": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "email"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": true
      },
      "CustomerDuplicateRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "kind",
          "status",
          "total",
          "processed",
          "found",
          "merged",
          "skipped",
          "failed",
          "failures",
          "error",
          "started_at",
          "finished_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_duplicate_run"
            ]
          },
          "id": {
            "type": "string",
            "example": "cdr_4fJ8sK2pQ9wLm3Xz"
          },
          "livemode": {
            "type": "boolean"
          },
          "kind": {
            "type": "string",
            "enum": [
              "scan",
              "merge_all"
            ],
            "description": "scan: looks for duplicates among the customers already in base. merge_all: merges every open proposal into its suggested customer."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "failed"
            ]
          },
          "total": {
            "type": "integer",
            "description": "scan: customers read. merge_all: open proposals at the start."
          },
          "processed": {
            "type": "integer",
            "description": "How many of total were gone through so far (real progress)."
          },
          "found": {
            "type": "integer",
            "description": "scan: new proposals created (pairs already proposed, dismissed or merged are never proposed again)."
          },
          "merged": {
            "type": "integer",
            "description": "merge_all: pairs merged into their suggested customer."
          },
          "skipped": {
            "type": "integer",
            "description": "merge_all: pairs decided meanwhile, or folded into another pair by an earlier merge of the run."
          },
          "failed": {
            "type": "integer",
            "description": "merge_all: pairs that could not be merged (see failures); the others went on."
          },
          "failures": {
            "type": "array",
            "description": "merge_all: each failed pair and why (the first 100).",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "duplicate",
                "customers",
                "reason"
              ],
              "properties": {
                "duplicate": {
                  "type": "string"
                },
                "customers": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "reason": {
                  "type": "string"
                }
              },
              "nullable": false
            }
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "failed: why the whole run stopped."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "CustomerDuplicateSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "open_count",
          "last_scanned_at",
          "scan",
          "merge_all"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_duplicate_summary"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "open_count": {
            "type": "integer",
            "description": "Open proposals of this mode."
          },
          "last_scanned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "End of the last finished scan of this mode (null: never scanned)."
          },
          "scan": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "object",
              "id",
              "livemode",
              "kind",
              "status",
              "total",
              "processed",
              "found",
              "merged",
              "skipped",
              "failed",
              "failures",
              "error",
              "started_at",
              "finished_at",
              "created_at"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "customer_duplicate_run"
                ]
              },
              "id": {
                "type": "string",
                "example": "cdr_4fJ8sK2pQ9wLm3Xz"
              },
              "livemode": {
                "type": "boolean"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "scan",
                  "merge_all"
                ],
                "description": "scan: looks for duplicates among the customers already in base. merge_all: merges every open proposal into its suggested customer."
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "done",
                  "failed"
                ]
              },
              "total": {
                "type": "integer",
                "description": "scan: customers read. merge_all: open proposals at the start."
              },
              "processed": {
                "type": "integer",
                "description": "How many of total were gone through so far (real progress)."
              },
              "found": {
                "type": "integer",
                "description": "scan: new proposals created (pairs already proposed, dismissed or merged are never proposed again)."
              },
              "merged": {
                "type": "integer",
                "description": "merge_all: pairs merged into their suggested customer."
              },
              "skipped": {
                "type": "integer",
                "description": "merge_all: pairs decided meanwhile, or folded into another pair by an earlier merge of the run."
              },
              "failed": {
                "type": "integer",
                "description": "merge_all: pairs that could not be merged (see failures); the others went on."
              },
              "failures": {
                "type": "array",
                "description": "merge_all: each failed pair and why (the first 100).",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "duplicate",
                    "customers",
                    "reason"
                  ],
                  "properties": {
                    "duplicate": {
                      "type": "string"
                    },
                    "customers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "nullable": false
                }
              },
              "error": {
                "type": "string",
                "nullable": true,
                "description": "failed: why the whole run stopped."
              },
              "started_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "nullable": true,
            "description": "The latest scan (null: never)."
          },
          "merge_all": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "object",
              "id",
              "livemode",
              "kind",
              "status",
              "total",
              "processed",
              "found",
              "merged",
              "skipped",
              "failed",
              "failures",
              "error",
              "started_at",
              "finished_at",
              "created_at"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "customer_duplicate_run"
                ]
              },
              "id": {
                "type": "string",
                "example": "cdr_4fJ8sK2pQ9wLm3Xz"
              },
              "livemode": {
                "type": "boolean"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "scan",
                  "merge_all"
                ],
                "description": "scan: looks for duplicates among the customers already in base. merge_all: merges every open proposal into its suggested customer."
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "done",
                  "failed"
                ]
              },
              "total": {
                "type": "integer",
                "description": "scan: customers read. merge_all: open proposals at the start."
              },
              "processed": {
                "type": "integer",
                "description": "How many of total were gone through so far (real progress)."
              },
              "found": {
                "type": "integer",
                "description": "scan: new proposals created (pairs already proposed, dismissed or merged are never proposed again)."
              },
              "merged": {
                "type": "integer",
                "description": "merge_all: pairs merged into their suggested customer."
              },
              "skipped": {
                "type": "integer",
                "description": "merge_all: pairs decided meanwhile, or folded into another pair by an earlier merge of the run."
              },
              "failed": {
                "type": "integer",
                "description": "merge_all: pairs that could not be merged (see failures); the others went on."
              },
              "failures": {
                "type": "array",
                "description": "merge_all: each failed pair and why (the first 100).",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "duplicate",
                    "customers",
                    "reason"
                  ],
                  "properties": {
                    "duplicate": {
                      "type": "string"
                    },
                    "customers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "nullable": false
                }
              },
              "error": {
                "type": "string",
                "nullable": true,
                "description": "failed: why the whole run stopped."
              },
              "started_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "nullable": true,
            "description": "The latest merge all (null: never)."
          }
        },
        "nullable": false
      },
      "Subscription": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "customer",
          "stripe_subscription_id",
          "external_id",
          "livemode",
          "status",
          "plan",
          "amount_cents",
          "currency",
          "interval",
          "interval_count",
          "mrr_cents",
          "current_period_end",
          "cancel_at_period_end",
          "canceled_at",
          "paused_until",
          "trial_end",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "subscription"
            ]
          },
          "id": {
            "type": "string",
            "example": "nsub_2mV9xYpL4aZf3kQ8"
          },
          "customer": {
            "type": "string"
          },
          "stripe_subscription_id": {
            "type": "string",
            "nullable": true
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "livemode": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "incomplete",
              "incomplete_expired",
              "trialing",
              "active",
              "past_due",
              "canceled",
              "unpaid",
              "paused"
            ]
          },
          "plan": {
            "type": "string",
            "nullable": true
          },
          "amount_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "interval": {
            "type": "string",
            "nullable": true
          },
          "interval_count": {
            "type": "integer"
          },
          "mrr_cents": {
            "type": "integer"
          },
          "current_period_end": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "canceled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "paused_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "trial_end": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Invoice": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "customer",
          "subscription",
          "stripe_invoice_id",
          "external_id",
          "livemode",
          "number",
          "status",
          "amount_due_cents",
          "amount_paid_cents",
          "amount_remaining_cents",
          "currency",
          "attempt_count",
          "next_attempt_at",
          "failure_code",
          "decline_code",
          "hosted_invoice_url",
          "paid_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "invoice"
            ]
          },
          "id": {
            "type": "string",
            "example": "ninv_9xYpL4aZf3kQ8mV2"
          },
          "customer": {
            "type": "string"
          },
          "subscription": {
            "type": "string",
            "nullable": true
          },
          "stripe_invoice_id": {
            "type": "string",
            "nullable": true
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "livemode": {
            "type": "boolean"
          },
          "number": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "open",
              "paid",
              "uncollectible",
              "void"
            ]
          },
          "amount_due_cents": {
            "type": "integer"
          },
          "amount_paid_cents": {
            "type": "integer"
          },
          "amount_remaining_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "attempt_count": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "failure_code": {
            "type": "string",
            "nullable": true
          },
          "decline_code": {
            "type": "string",
            "nullable": true
          },
          "hosted_invoice_url": {
            "type": "string",
            "nullable": true
          },
          "paid_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "PaymentMethod": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "customer",
          "stripe_payment_method_id",
          "livemode",
          "type",
          "brand",
          "last4",
          "exp_month",
          "exp_year",
          "funding",
          "country",
          "wallet",
          "is_default",
          "detached_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "payment_method"
            ]
          },
          "id": {
            "type": "string",
            "example": "npm_L4aZf3kQ8mV29xYp"
          },
          "customer": {
            "type": "string",
            "nullable": true
          },
          "stripe_payment_method_id": {
            "type": "string",
            "nullable": true
          },
          "livemode": {
            "type": "boolean"
          },
          "type": {
            "type": "string",
            "example": "card"
          },
          "brand": {
            "type": "string",
            "nullable": true
          },
          "last4": {
            "type": "string",
            "nullable": true
          },
          "exp_month": {
            "type": "integer",
            "nullable": true
          },
          "exp_year": {
            "type": "integer",
            "nullable": true
          },
          "funding": {
            "type": "string",
            "nullable": true,
            "description": "credit, debit, prepaid or unknown"
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "wallet": {
            "type": "string",
            "nullable": true,
            "description": "apple_pay, google_pay, link…"
          },
          "is_default": {
            "type": "boolean"
          },
          "detached_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false,
        "description": "Metadata only. NoChurn never stores card numbers."
      },
      "Setup": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "billing_source",
          "api_base_url",
          "complete",
          "steps",
          "onboarding",
          "counters",
          "history_import"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "setup"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "billing_source": {
            "type": "string",
            "enum": [
              "stripe",
              "api"
            ]
          },
          "api_base_url": {
            "type": "string",
            "description": "Public URL of the API (e.g. https://api.nochurn.sh), for the dashboard's copy-paste examples."
          },
          "complete": {
            "type": "boolean",
            "description": "Every step done, or skipped (set up later)."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "done",
                "skipped",
                "fix"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "billing_connected",
                    "billing_synced",
                    "history_imported",
                    "webhook_received",
                    "api_key_created",
                    "first_api_call",
                    "support_connected",
                    "cancel_modal_detected",
                    "email_domain_verified",
                    "cancel_flow_published",
                    "recovery_retries_configured",
                    "prevention_signals_enabled"
                  ]
                },
                "done": {
                  "type": "boolean"
                },
                "skipped": {
                  "type": "boolean"
                },
                "fix": {
                  "type": "string",
                  "nullable": true
                }
              },
              "nullable": false
            }
          },
          "onboarding": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "step",
              "skipped",
              "completed_at"
            ],
            "properties": {
              "step": {
                "type": "string",
                "enum": [
                  "billing",
                  "support",
                  "cancel_modal",
                  "email_domain",
                  "done"
                ]
              },
              "skipped": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "completed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false
          },
          "counters": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "customers_imported",
              "active_customers",
              "mrr",
              "failed_payments_to_recover",
              "high_risk_customers",
              "scored_customers",
              "first_api_call_at",
              "customers_with_multiple_subscriptions",
              "sessions_without_subscription_30d"
            ],
            "properties": {
              "customers_imported": {
                "type": "integer"
              },
              "active_customers": {
                "type": "integer"
              },
              "mrr": {
                "type": "array",
                "description": "One entry per currency, largest first. Never converted.",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "currency",
                    "cents"
                  ],
                  "properties": {
                    "currency": {
                      "type": "string"
                    },
                    "cents": {
                      "type": "integer"
                    }
                  },
                  "nullable": false
                }
              },
              "failed_payments_to_recover": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "count",
                  "amounts"
                ],
                "properties": {
                  "count": {
                    "type": "integer"
                  },
                  "amounts": {
                    "type": "array",
                    "description": "One entry per currency, largest first. Never converted.",
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "currency",
                        "cents"
                      ],
                      "properties": {
                        "currency": {
                          "type": "string"
                        },
                        "cents": {
                          "type": "integer"
                        }
                      },
                      "nullable": false
                    }
                  }
                },
                "nullable": false,
                "description": "Open invoices with at least one failed attempt."
              },
              "high_risk_customers": {
                "type": "integer"
              },
              "scored_customers": {
                "type": "integer",
                "description": "0 until churn scores are computed (M4)."
              },
              "first_api_call_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "customers_with_multiple_subscriptions": {
                "type": "integer",
                "description": "Customers with two or more live subscriptions in this mode."
              },
              "sessions_without_subscription_30d": {
                "type": "integer",
                "description": "Cancel sessions of the last 30 days opened without a `subscription` (token claim or server request). With customers_with_multiple_subscriptions > 0, sign the token with `subscription` (DECISIONS 72)."
              }
            },
            "nullable": false
          },
          "history_import": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "since",
              "progress",
              "error",
              "started_at",
              "finished_at"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "running",
                  "done",
                  "failed"
                ]
              },
              "since": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "progress": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                }
              },
              "error": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "message",
                  "fix",
                  "docs_url"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "enum": [
                      "import_access_denied",
                      "import_interrupted",
                      "import_failed"
                    ]
                  },
                  "message": {
                    "type": "string"
                  },
                  "fix": {
                    "type": "string"
                  },
                  "docs_url": {
                    "type": "string"
                  }
                },
                "nullable": true,
                "description": "Why the import stopped and what to do; null unless it failed."
              },
              "started_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": true,
            "description": "12-month Stripe history import of the latest connection (null with billing by API)."
          }
        },
        "nullable": false
      },
      "StripeAuthorizeUrl": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "url",
          "livemode"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "stripe_authorize_url"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "livemode": {
            "type": "boolean"
          }
        },
        "nullable": false
      },
      "Membership": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "role",
          "user",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "membership"
            ]
          },
          "id": {
            "type": "string",
            "example": "mbr_Q2mV9xYpL4aZf3k8"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "user": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "email",
              "name",
              "two_factor_enabled",
              "last_login_at"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "email": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "two_factor_enabled": {
                "type": "boolean"
              },
              "last_login_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Invitation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "email",
          "role",
          "status",
          "url",
          "invited_by",
          "expires_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "invitation"
            ]
          },
          "id": {
            "type": "string",
            "example": "inv_V9xYpL4aZf3kQ8m2"
          },
          "email": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "revoked",
              "expired"
            ]
          },
          "url": {
            "type": "string",
            "nullable": true,
            "description": "Invitation link. Only returned at creation: copy it and send it yourself."
          },
          "invited_by": {
            "type": "string",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "LogEntry": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "occurred_at",
          "source",
          "type",
          "customer",
          "summary",
          "status",
          "error",
          "why",
          "ref",
          "request_id"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "log_entry"
            ]
          },
          "id": {
            "type": "string",
            "example": "log_3kH9sQ2pVb7LmXz1"
          },
          "livemode": {
            "type": "boolean"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "Millisecond precision."
          },
          "source": {
            "type": "string",
            "enum": [
              "stripe",
              "api",
              "nochurn",
              "widget",
              "webhook",
              "chargebee",
              "recurly",
              "maxio",
              "zuora",
              "paddle",
              "lemon_squeezy",
              "polar",
              "braintree",
              "mollie",
              "gocardless",
              "razorpay",
              "app_store",
              "google_play",
              "custom",
              "intercom",
              "crisp",
              "zendesk",
              "hubspot",
              "helpscout",
              "gorgias"
            ],
            "description": "stripe (webhooks), api (your calls with an API key), nochurn (decisions), widget, webhook (outgoing), a billing tool (chargebee, zuora…: its webhooks and what NoChurn reads from it), a support tool (intercom, crisp, zendesk, hubspot, helpscout, gorgias)."
          },
          "type": {
            "type": "string",
            "example": "invoice.payment_failed"
          },
          "customer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "cst_4fJ8sK2pQ9wLm3Xz"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true,
            "description": "The customer this line is about (open it with /v1/customers/{id})."
          },
          "summary": {
            "type": "string",
            "nullable": true,
            "example": "$49.00 · insufficient_funds"
          },
          "status": {
            "type": "string",
            "example": "received",
            "description": "HTTP status (\"200\", \"500\") or ok, received, ignored, failed."
          },
          "error": {
            "type": "boolean"
          },
          "why": {
            "type": "string",
            "nullable": true,
            "description": "Why this happened (the reason of the NoChurn decision)."
          },
          "ref": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "example": "event"
              },
              "id": {
                "type": "string",
                "example": "nevt_tZCYqtEDVAvwZjD5"
              }
            },
            "nullable": true
          },
          "request_id": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": false
      },
      "LogEntryPage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor",
          "poll_cursor",
          "retention_days"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LogEntry"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "With after=: more new lines are waiting, poll again now."
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get older lines."
          },
          "poll_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as after to get only newer lines (live view)."
          },
          "retention_days": {
            "type": "integer",
            "example": 90
          }
        },
        "nullable": false
      },
      "LogStats": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "timezone",
          "today_count",
          "errors_count",
          "retention_days"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "log_stats"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "timezone": {
            "type": "string",
            "example": "Europe/Paris"
          },
          "today_count": {
            "type": "integer"
          },
          "errors_count": {
            "type": "integer",
            "minimum": 0,
            "example": 3,
            "description": "Error lines in the window and filters passed (same as GET /v1/logs: 24 hours by default)."
          },
          "retention_days": {
            "type": "integer",
            "example": 90
          }
        },
        "nullable": false
      },
      "PaymentMethodSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "type",
          "brand",
          "last4",
          "exp_month",
          "exp_year",
          "funding",
          "wallet",
          "risk_category",
          "label"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "npm_7Gh2kL9pQs3VbX1z"
          },
          "type": {
            "type": "string",
            "example": "card"
          },
          "brand": {
            "type": "string",
            "nullable": true,
            "example": "visa"
          },
          "last4": {
            "type": "string",
            "nullable": true,
            "example": "4242"
          },
          "exp_month": {
            "type": "integer",
            "nullable": true
          },
          "exp_year": {
            "type": "integer",
            "nullable": true
          },
          "funding": {
            "type": "string",
            "nullable": true,
            "example": "prepaid"
          },
          "wallet": {
            "type": "string",
            "nullable": true,
            "example": "apple_pay"
          },
          "risk_category": {
            "type": "string",
            "nullable": true,
            "enum": [
              "bank_card",
              "wallet",
              "link",
              "sepa",
              "neobank",
              "prepaid",
              "disposable",
              null
            ]
          },
          "label": {
            "type": "string",
            "example": "Apple Pay · Visa •••• 4242"
          }
        },
        "nullable": true,
        "description": "Default payment method (null when none)."
      },
      "CustomerPage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Customer"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          },
          "total_count": {
            "type": "integer",
            "description": "With include[]=total_count."
          }
        },
        "nullable": false
      },
      "CustomerSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "timezone",
          "month_start",
          "active",
          "high_risk",
          "in_recovery",
          "saved_this_month"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_summary"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "timezone": {
            "type": "string",
            "example": "Europe/Paris"
          },
          "month_start": {
            "type": "string",
            "format": "date-time"
          },
          "active": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "count",
              "new_this_month"
            ],
            "properties": {
              "count": {
                "type": "integer"
              },
              "new_this_month": {
                "type": "integer"
              }
            },
            "nullable": false
          },
          "high_risk": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "threshold",
              "count",
              "mrr",
              "scored_customers"
            ],
            "properties": {
              "threshold": {
                "type": "integer"
              },
              "count": {
                "type": "integer"
              },
              "mrr": {
                "type": "array",
                "description": "One entry per currency, largest first. Never converted.",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "currency",
                    "cents"
                  ],
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "eur"
                    },
                    "cents": {
                      "type": "integer"
                    }
                  },
                  "nullable": false
                }
              },
              "scored_customers": {
                "type": "integer",
                "description": "Active customers with a score."
              }
            },
            "nullable": false
          },
          "in_recovery": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "count",
              "mrr"
            ],
            "properties": {
              "count": {
                "type": "integer"
              },
              "mrr": {
                "type": "array",
                "description": "One entry per currency, largest first. Never converted.",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "currency",
                    "cents"
                  ],
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "eur"
                    },
                    "cents": {
                      "type": "integer"
                    }
                  },
                  "nullable": false
                }
              }
            },
            "nullable": false
          },
          "saved_this_month": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "count",
              "unavailable_reason"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "nullable": true
              },
              "unavailable_reason": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "CustomerProfile": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "billing",
          "score",
          "ai_analysis",
          "ai_analysis_status",
          "attributes",
          "links"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_profile"
            ]
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "billing": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "mrr_cents",
              "currency",
              "lifetime_value_cents",
              "lifetime_value_since",
              "customer_since",
              "payment_method",
              "next_renewal",
              "subscriptions"
            ],
            "properties": {
              "mrr_cents": {
                "type": "integer"
              },
              "currency": {
                "type": "string",
                "nullable": true
              },
              "lifetime_value_cents": {
                "type": "integer"
              },
              "lifetime_value_since": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Start of the history the lifetime value covers."
              },
              "customer_since": {
                "type": "string",
                "format": "date-time"
              },
              "payment_method": {
                "$ref": "#/components/schemas/PaymentMethodSummary"
              },
              "next_renewal": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "at",
                  "amount_cents",
                  "currency",
                  "subscription"
                ],
                "properties": {
                  "at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "amount_cents": {
                    "type": "integer"
                  },
                  "currency": {
                    "type": "string",
                    "nullable": true
                  },
                  "subscription": {
                    "type": "string"
                  }
                },
                "nullable": true
              },
              "subscriptions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "plan",
                    "status",
                    "mrr_cents",
                    "currency",
                    "interval",
                    "interval_count",
                    "current_period_end",
                    "cancel_at_period_end"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "plan": {
                      "type": "string",
                      "nullable": true
                    },
                    "status": {
                      "type": "string"
                    },
                    "mrr_cents": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string",
                      "nullable": true
                    },
                    "interval": {
                      "type": "string",
                      "nullable": true
                    },
                    "interval_count": {
                      "type": "integer"
                    },
                    "current_period_end": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "cancel_at_period_end": {
                      "type": "boolean"
                    }
                  },
                  "nullable": false
                }
              }
            },
            "nullable": false
          },
          "score": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "score",
              "level",
              "factors",
              "computed_at"
            ],
            "properties": {
              "score": {
                "type": "integer",
                "nullable": true
              },
              "level": {
                "type": "string",
                "nullable": true
              },
              "factors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "key",
                    "label",
                    "points"
                  ],
                  "properties": {
                    "key": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "points": {
                      "type": "integer"
                    }
                  },
                  "nullable": false
                }
              },
              "computed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "nullable": false,
            "description": "Factors are computed with the churn score (M4)."
          },
          "ai_analysis": {
            "$ref": "#/components/schemas/CustomerInsight"
          },
          "ai_analysis_status": {
            "type": "string",
            "enum": [
              "fresh",
              "stale",
              "missing",
              "not_enough_data"
            ],
            "description": "fresh: less than 7 days old and no important event since (POST …/insight returns it without an AI call). stale: older, or an important event came after it (ai_analysis is the old one). missing: never analysed. not_enough_data: too little activity for an analysis (POST …/insight answers 422 insufficient_data)."
          },
          "attributes": {
            "type": "object",
            "additionalProperties": true
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "stripe_dashboard"
            ],
            "properties": {
              "stripe_dashboard": {
                "type": "string",
                "nullable": true,
                "format": "uri"
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "TimelinePage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "object",
                "id",
                "occurred_at",
                "category",
                "type",
                "summary",
                "ref",
                "data"
              ],
              "properties": {
                "object": {
                  "type": "string",
                  "enum": [
                    "timeline_item"
                  ]
                },
                "id": {
                  "type": "string",
                  "example": "npay_2Lk9sQ7vBn3XmP4a"
                },
                "occurred_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "category": {
                  "type": "string",
                  "enum": [
                    "billing",
                    "flow",
                    "support",
                    "product",
                    "account"
                  ]
                },
                "type": {
                  "type": "string",
                  "example": "payment.failed"
                },
                "summary": {
                  "type": "string",
                  "nullable": true,
                  "example": "€49.00 · failed · insufficient_funds"
                },
                "ref": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "type",
                    "id"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "nullable": true
                    },
                    "id": {
                      "type": "string"
                    }
                  },
                  "nullable": true
                },
                "data": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "nullable": false
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": false
      },
      "Flow": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "kind",
          "key",
          "name",
          "status",
          "holdout_percent",
          "test_version",
          "live_version",
          "unpublished_changes",
          "mode_status",
          "draft_updated_at",
          "paused_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow"
            ]
          },
          "id": {
            "type": "string",
            "example": "flw_3Ls9dK2mQx7VbN4p"
          },
          "kind": {
            "type": "string",
            "enum": [
              "prevention",
              "recovery",
              "cancel",
              "winback"
            ]
          },
          "key": {
            "type": "string",
            "example": "default"
          },
          "name": {
            "type": "string",
            "example": "Recovery flow"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "test",
              "live",
              "paused"
            ],
            "description": "draft: never published; test: runs in test mode only; live: runs in both modes; paused."
          },
          "holdout_percent": {
            "type": "integer",
            "description": "Share of customers NoChurn leaves alone, to measure its effect."
          },
          "test_version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "number",
              "published_at",
              "published_to"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "flv_9Kd2mX7pQs4LzA1b"
              },
              "number": {
                "type": "integer"
              },
              "published_at": {
                "type": "string",
                "format": "date-time"
              },
              "published_to": {
                "type": "string",
                "enum": [
                  "test",
                  "live"
                ]
              }
            },
            "nullable": true
          },
          "live_version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "number",
              "published_at",
              "published_to"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "flv_9Kd2mX7pQs4LzA1b"
              },
              "number": {
                "type": "integer"
              },
              "published_at": {
                "type": "string",
                "format": "date-time"
              },
              "published_to": {
                "type": "string",
                "enum": [
                  "test",
                  "live"
                ]
              }
            },
            "nullable": true
          },
          "unpublished_changes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Draft sections that differ from the last version."
          },
          "mode_status": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "livemode",
              "state",
              "version",
              "published_at",
              "unpublished_changes"
            ],
            "properties": {
              "livemode": {
                "type": "boolean"
              },
              "state": {
                "type": "string",
                "enum": [
                  "published",
                  "draft",
                  "paused"
                ],
                "description": "published: a version runs in this mode; draft: nothing published in this mode yet; paused: paused (both modes)."
              },
              "version": {
                "type": "integer",
                "nullable": true,
                "description": "Number of the version that runs in this mode."
              },
              "published_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "unpublished_changes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Draft sections not yet published in this mode (a pending draft)."
              }
            },
            "nullable": false,
            "description": "Where the flow stands in the mode of the request (NoChurn-Mode or the key's mode)."
          },
          "draft_updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "paused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "FlowDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "kind",
          "key",
          "name",
          "status",
          "holdout_percent",
          "test_version",
          "live_version",
          "unpublished_changes",
          "mode_status",
          "draft_updated_at",
          "paused_at",
          "created_at",
          "updated_at",
          "draft",
          "logos"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow"
            ]
          },
          "id": {
            "type": "string",
            "example": "flw_3Ls9dK2mQx7VbN4p"
          },
          "kind": {
            "type": "string",
            "enum": [
              "prevention",
              "recovery",
              "cancel",
              "winback"
            ]
          },
          "key": {
            "type": "string",
            "example": "default"
          },
          "name": {
            "type": "string",
            "example": "Recovery flow"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "test",
              "live",
              "paused"
            ],
            "description": "draft: never published; test: runs in test mode only; live: runs in both modes; paused."
          },
          "holdout_percent": {
            "type": "integer",
            "description": "Share of customers NoChurn leaves alone, to measure its effect."
          },
          "test_version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "number",
              "published_at",
              "published_to"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "flv_9Kd2mX7pQs4LzA1b"
              },
              "number": {
                "type": "integer"
              },
              "published_at": {
                "type": "string",
                "format": "date-time"
              },
              "published_to": {
                "type": "string",
                "enum": [
                  "test",
                  "live"
                ]
              }
            },
            "nullable": true
          },
          "live_version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "number",
              "published_at",
              "published_to"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "flv_9Kd2mX7pQs4LzA1b"
              },
              "number": {
                "type": "integer"
              },
              "published_at": {
                "type": "string",
                "format": "date-time"
              },
              "published_to": {
                "type": "string",
                "enum": [
                  "test",
                  "live"
                ]
              }
            },
            "nullable": true
          },
          "unpublished_changes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Draft sections that differ from the last version."
          },
          "mode_status": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "livemode",
              "state",
              "version",
              "published_at",
              "unpublished_changes"
            ],
            "properties": {
              "livemode": {
                "type": "boolean"
              },
              "state": {
                "type": "string",
                "enum": [
                  "published",
                  "draft",
                  "paused"
                ],
                "description": "published: a version runs in this mode; draft: nothing published in this mode yet; paused: paused (both modes)."
              },
              "version": {
                "type": "integer",
                "nullable": true,
                "description": "Number of the version that runs in this mode."
              },
              "published_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "unpublished_changes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Draft sections not yet published in this mode (a pending draft)."
              }
            },
            "nullable": false,
            "description": "Where the flow stands in the mode of the request (NoChurn-Mode or the key's mode)."
          },
          "draft_updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "paused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "draft": {
            "type": "object",
            "additionalProperties": true,
            "description": "Configuration being edited, by section."
          },
          "logos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CancelFlowLogo"
            },
            "description": "Logos uploaded for the cancel modal and not removed (POST /v1/flows/cancel/logos); [] for the other flows."
          }
        },
        "nullable": false
      },
      "FlowVersion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "flow",
          "number",
          "config",
          "holdout_percent",
          "published_to",
          "published_by",
          "published_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow_version"
            ]
          },
          "id": {
            "type": "string"
          },
          "flow": {
            "type": "string"
          },
          "number": {
            "type": "integer"
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "holdout_percent": {
            "type": "integer"
          },
          "published_to": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "published_by": {
            "type": "string",
            "nullable": true
          },
          "published_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "FlowRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "flow",
          "version",
          "customer",
          "subject",
          "holdout",
          "state",
          "current_step",
          "outcome",
          "outcome_at",
          "outcome_mrr_cents",
          "outcome_cents",
          "currency",
          "confirmation",
          "stop_reason",
          "started_at",
          "ended_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow_run"
            ]
          },
          "id": {
            "type": "string",
            "example": "run_7Hk2sQ9vLp3XmB4a"
          },
          "livemode": {
            "type": "boolean"
          },
          "flow": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "customer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "email",
              "mrr_cents",
              "currency"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              },
              "mrr_cents": {
                "type": "integer"
              },
              "currency": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true
          },
          "subject": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "nullable": true
              },
              "id": {
                "type": "string"
              }
            },
            "nullable": true
          },
          "holdout": {
            "type": "boolean",
            "description": "In the holdout group: NoChurn does nothing, the run is only measured."
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "succeeded",
              "failed",
              "stopped"
            ]
          },
          "current_step": {
            "type": "string",
            "nullable": true
          },
          "outcome": {
            "type": "string",
            "nullable": true,
            "enum": [
              "prevented",
              "recovered",
              "saved",
              "won_back",
              "lost",
              "cancelled",
              "abandoned",
              null
            ]
          },
          "outcome_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "outcome_mrr_cents": {
            "type": "integer",
            "nullable": true
          },
          "outcome_cents": {
            "type": "integer",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "confirmation": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "confirmed",
              "reverted",
              null
            ],
            "description": "pending, then confirmed if the customer still pays 30 days later, else reverted."
          },
          "stop_reason": {
            "type": "string",
            "nullable": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "nullable": false
      },
      "SendSchedule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "audience",
          "overrides",
          "effective",
          "presets"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "send_schedule"
            ]
          },
          "audience": {
            "type": "string",
            "nullable": true,
            "enum": [
              "b2b",
              "b2c",
              null
            ]
          },
          "overrides": {
            "type": "object",
            "additionalProperties": true,
            "description": "The account's own options on top of the preset.",
            "properties": {
              "audience": {
                "type": "string",
                "enum": [
                  "b2b",
                  "b2c"
                ]
              },
              "respect_customer_timezone": {
                "type": "boolean"
              },
              "skip_weekends": {
                "type": "boolean"
              },
              "skip_public_holidays": {
                "type": "boolean",
                "description": "Holidays of the customer's country (and state)."
              },
              "windows": {
                "type": "object",
                "properties": {
                  "weekday": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "weekend": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "quiet_hours": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "days_off": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "date"
                }
              }
            }
          },
          "effective": {
            "type": "object",
            "additionalProperties": true,
            "description": "What applies (before a flow's own overrides).",
            "properties": {
              "audience": {
                "type": "string",
                "enum": [
                  "b2b",
                  "b2c"
                ]
              },
              "respect_customer_timezone": {
                "type": "boolean"
              },
              "skip_weekends": {
                "type": "boolean"
              },
              "skip_public_holidays": {
                "type": "boolean",
                "description": "Holidays of the customer's country (and state)."
              },
              "windows": {
                "type": "object",
                "properties": {
                  "weekday": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "weekend": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "quiet_hours": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "days_off": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "date"
                }
              }
            }
          },
          "presets": {
            "type": "object",
            "required": [
              "b2b",
              "b2c"
            ],
            "additionalProperties": false,
            "description": "Every option's default, per audience (the preset, then the defaults common to both: time zone, quiet hours, days off). An option equal to it is sent as null to follow the preset.",
            "properties": {
              "b2b": {
                "type": "object",
                "additionalProperties": true,
                "description": "B2B preset.",
                "properties": {
                  "audience": {
                    "type": "string",
                    "enum": [
                      "b2b",
                      "b2c"
                    ]
                  },
                  "respect_customer_timezone": {
                    "type": "boolean"
                  },
                  "skip_weekends": {
                    "type": "boolean"
                  },
                  "skip_public_holidays": {
                    "type": "boolean",
                    "description": "Holidays of the customer's country (and state)."
                  },
                  "windows": {
                    "type": "object",
                    "properties": {
                      "weekday": {
                        "type": "array",
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      },
                      "weekend": {
                        "type": "array",
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "quiet_hours": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "from": {
                        "type": "string"
                      },
                      "to": {
                        "type": "string"
                      }
                    }
                  },
                  "days_off": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                },
                "required": [
                  "respect_customer_timezone",
                  "skip_weekends",
                  "skip_public_holidays",
                  "windows",
                  "quiet_hours",
                  "days_off"
                ]
              },
              "b2c": {
                "type": "object",
                "additionalProperties": true,
                "description": "B2C preset.",
                "properties": {
                  "audience": {
                    "type": "string",
                    "enum": [
                      "b2b",
                      "b2c"
                    ]
                  },
                  "respect_customer_timezone": {
                    "type": "boolean"
                  },
                  "skip_weekends": {
                    "type": "boolean"
                  },
                  "skip_public_holidays": {
                    "type": "boolean",
                    "description": "Holidays of the customer's country (and state)."
                  },
                  "windows": {
                    "type": "object",
                    "properties": {
                      "weekday": {
                        "type": "array",
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      },
                      "weekend": {
                        "type": "array",
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "quiet_hours": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "from": {
                        "type": "string"
                      },
                      "to": {
                        "type": "string"
                      }
                    }
                  },
                  "days_off": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                },
                "required": [
                  "respect_customer_timezone",
                  "skip_weekends",
                  "skip_public_holidays",
                  "windows",
                  "quiet_hours",
                  "days_off"
                ]
              }
            }
          },
          "suggested_audience": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "audience",
              "reason",
              "sample_size",
              "consumer_share"
            ],
            "properties": {
              "audience": {
                "type": "string",
                "enum": [
                  "b2b",
                  "b2c"
                ]
              },
              "reason": {
                "type": "string",
                "example": "82% of your 140 customers use company addresses"
              },
              "sample_size": {
                "type": "integer"
              },
              "consumer_share": {
                "type": "number",
                "example": 0.18
              }
            },
            "nullable": true,
            "description": "Suggestion from the customers' email domains (onboarding step 1); null below 20 customers with an email."
          }
        },
        "nullable": false
      },
      "SendSlot": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "at",
          "timezone",
          "timezone_source",
          "delayed",
          "reasons",
          "audience"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "send_slot"
            ]
          },
          "customer": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "null when the send window allows no moment in the next 31 days"
          },
          "timezone": {
            "type": "string",
            "example": "Europe/Berlin"
          },
          "timezone_source": {
            "type": "string",
            "nullable": true
          },
          "delayed": {
            "type": "boolean"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "weekend"
            ]
          },
          "audience": {
            "type": "string",
            "enum": [
              "b2b",
              "b2c"
            ]
          }
        },
        "nullable": false
      },
      "SendingDomain": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "domain",
          "sender_name",
          "sender_email",
          "sending_host",
          "status",
          "provider",
          "provisioning_error",
          "verified_at",
          "last_checked_at",
          "existing_dmarc",
          "fallback_address",
          "from_address",
          "dns_records",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "sending_domain"
            ]
          },
          "id": {
            "type": "string",
            "example": "sdm_4Hs8dK2mQx7VbN4p"
          },
          "domain": {
            "type": "string",
            "example": "acme.com"
          },
          "sender_name": {
            "type": "string",
            "nullable": true
          },
          "sender_email": {
            "type": "string",
            "example": "billing@acme.com"
          },
          "sending_host": {
            "type": "string",
            "example": "nc-x7k2.acme.com"
          },
          "status": {
            "type": "string",
            "enum": [
              "provisioning",
              "pending",
              "verified",
              "failed"
            ],
            "description": "provisioning: Mailgun is preparing the records (dns_records is empty until then, or provisioning_error says why it could not); pending: records to add; verified: records found and the domain active at Mailgun; failed: records still missing after 7 days."
          },
          "provider": {
            "type": "string",
            "enum": [
              "mailgun",
              "fake"
            ],
            "description": "Who holds the domain: mailgun, or fake outside production (sample records, nothing created)."
          },
          "provisioning_error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message",
              "fix",
              "docs_url"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "email_domain_provisioning_failed"
                ]
              },
              "message": {
                "type": "string"
              },
              "fix": {
                "type": "string"
              },
              "docs_url": {
                "type": "string"
              }
            },
            "nullable": true,
            "description": "Why the records could not be prepared yet, with what to do; null otherwise."
          },
          "verified_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "existing_dmarc": {
            "type": "boolean",
            "description": "A DMARC record already exists: NoChurn does not propose another one."
          },
          "fallback_address": {
            "type": "string",
            "example": "acme@mail.nochurn.sh"
          },
          "from_address": {
            "type": "string",
            "description": "Emails go out from this address now: the sender once verified, the fallback before."
          },
          "dns_records": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "type",
                "host",
                "value",
                "purpose",
                "optional",
                "status",
                "found",
                "checked_at"
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "MX",
                    "TXT",
                    "CNAME"
                  ]
                },
                "host": {
                  "type": "string",
                  "example": "nc-x7k2.acme.com"
                },
                "value": {
                  "type": "string",
                  "example": "10 mxa.eu.mailgun.org"
                },
                "purpose": {
                  "type": "string",
                  "example": "replies, bounces"
                },
                "optional": {
                  "type": "boolean"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "verified"
                  ]
                },
                "found": {
                  "type": "string",
                  "nullable": true
                },
                "checked_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              },
              "nullable": false
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Message": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "channel",
          "customer",
          "flow_run",
          "template_key",
          "locale",
          "from",
          "from_name",
          "reply_to",
          "to",
          "subject",
          "ai_generated",
          "review_state",
          "review_issues",
          "reviewed_at",
          "status",
          "scheduled_for",
          "schedule_reasons",
          "sent_at",
          "delivered_at",
          "opened_at",
          "clicked_at",
          "bounced_at",
          "error",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "message"
            ]
          },
          "id": {
            "type": "string",
            "example": "msg_8Hd2kQ9xLm3VbN7p"
          },
          "livemode": {
            "type": "boolean"
          },
          "channel": {
            "type": "string",
            "enum": [
              "email"
            ]
          },
          "customer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": false
          },
          "flow_run": {
            "type": "string",
            "nullable": true
          },
          "template_key": {
            "type": "string",
            "nullable": true
          },
          "locale": {
            "type": "string",
            "nullable": true
          },
          "from": {
            "type": "string",
            "example": "billing@acme.com"
          },
          "from_name": {
            "type": "string",
            "nullable": true
          },
          "reply_to": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "ai_generated": {
            "type": "boolean"
          },
          "review_state": {
            "type": "string",
            "enum": [
              "auto_approved",
              "needs_review",
              "approved",
              "rejected"
            ]
          },
          "review_issues": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Why the automatic review kept it for a human."
          },
          "reviewed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "sending",
              "sent",
              "delivered",
              "bounced",
              "complained",
              "failed",
              "suppressed",
              "cancelled"
            ]
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time"
          },
          "schedule_reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "opened_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "clicked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "bounced_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "MessageDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "channel",
          "customer",
          "flow_run",
          "template_key",
          "locale",
          "from",
          "from_name",
          "reply_to",
          "to",
          "subject",
          "ai_generated",
          "review_state",
          "review_issues",
          "reviewed_at",
          "status",
          "scheduled_for",
          "schedule_reasons",
          "sent_at",
          "delivered_at",
          "opened_at",
          "clicked_at",
          "bounced_at",
          "error",
          "created_at",
          "body_text",
          "body_html"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "message"
            ]
          },
          "id": {
            "type": "string",
            "example": "msg_8Hd2kQ9xLm3VbN7p"
          },
          "livemode": {
            "type": "boolean"
          },
          "channel": {
            "type": "string",
            "enum": [
              "email"
            ]
          },
          "customer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": false
          },
          "flow_run": {
            "type": "string",
            "nullable": true
          },
          "template_key": {
            "type": "string",
            "nullable": true
          },
          "locale": {
            "type": "string",
            "nullable": true
          },
          "from": {
            "type": "string",
            "example": "billing@acme.com"
          },
          "from_name": {
            "type": "string",
            "nullable": true
          },
          "reply_to": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "ai_generated": {
            "type": "boolean"
          },
          "review_state": {
            "type": "string",
            "enum": [
              "auto_approved",
              "needs_review",
              "approved",
              "rejected"
            ]
          },
          "review_issues": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Why the automatic review kept it for a human."
          },
          "reviewed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "sending",
              "sent",
              "delivered",
              "bounced",
              "complained",
              "failed",
              "suppressed",
              "cancelled"
            ]
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time"
          },
          "schedule_reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "opened_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "clicked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "bounced_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "body_text": {
            "type": "string"
          },
          "body_html": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": false
      },
      "EmailSuppression": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "email",
          "reason",
          "scope",
          "message",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "email_suppression"
            ]
          },
          "id": {
            "type": "string",
            "example": "sup_2Kd9xLm3VbN7pQ4s"
          },
          "livemode": {
            "type": "boolean"
          },
          "email": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "enum": [
              "unsubscribed",
              "bounced",
              "complained",
              "gdpr",
              "manual"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "all",
              "marketing"
            ]
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "SetupShare": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "topic",
          "email",
          "sent_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "setup_share"
            ]
          },
          "topic": {
            "type": "string",
            "enum": [
              "email_domain",
              "api",
              "cancel_modal"
            ]
          },
          "email": {
            "type": "string"
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "FlowTestEmail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "to",
          "subject",
          "body",
          "ai_generated",
          "source",
          "review"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow_test_email"
            ]
          },
          "to": {
            "type": "string"
          },
          "subject": {
            "type": "string",
            "example": "[TEST] Your payment didn't go through"
          },
          "body": {
            "type": "string"
          },
          "ai_generated": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "custom",
              "ai",
              "template"
            ],
            "description": "custom: your text; ai: written by the AI; template: NoChurn's text."
          },
          "review": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "state",
              "issues"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "auto_approved",
                  "needs_review"
                ]
              },
              "issues": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "EmailPreview": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "step",
          "label",
          "language",
          "source",
          "subject",
          "text",
          "html",
          "ai_generated",
          "review",
          "warnings",
          "customer",
          "sample",
          "template",
          "custom",
          "placeholders",
          "required_placeholders",
          "languages",
          "sources"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "email_preview"
            ]
          },
          "step": {
            "type": "string",
            "example": "email_1",
            "description": "email_1…, card_removed, card_expiring, renewal_reminder, d30, d60, d90, review_follow_up"
          },
          "label": {
            "type": "string",
            "example": "Email 1 · day 0"
          },
          "language": {
            "type": "string",
            "example": "en"
          },
          "source": {
            "type": "string",
            "enum": [
              "custom",
              "ai",
              "template"
            ],
            "description": "Where the text shown comes from."
          },
          "subject": {
            "type": "string",
            "description": "Placeholders filled."
          },
          "text": {
            "type": "string",
            "description": "Plain-text body, placeholders filled."
          },
          "html": {
            "type": "string",
            "description": "HTML body as sent (escaped paragraphs and links, no script, no style)."
          },
          "ai_generated": {
            "type": "boolean"
          },
          "review": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "state",
              "issues"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "auto_approved",
                  "needs_review"
                ]
              },
              "issues": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "nullable": false
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the preview differs on, and problems of your text."
          },
          "customer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "example"
            ],
            "properties": {
              "id": {
                "type": "string",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "example": {
                "type": "boolean"
              }
            },
            "nullable": false,
            "description": "Who the placeholders were filled for: the example customer (fictional data) or a real customer."
          },
          "sample": {
            "type": "boolean",
            "description": "True when the data is the example customer's."
          },
          "template": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "subject",
              "body"
            ],
            "properties": {
              "subject": {
                "type": "string"
              },
              "body": {
                "type": "string"
              }
            },
            "nullable": false,
            "description": "NoChurn's text in this language, placeholders not filled (to start from or reset to)."
          },
          "custom": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "subject",
              "body"
            ],
            "properties": {
              "subject": {
                "type": "string"
              },
              "body": {
                "type": "string"
              }
            },
            "nullable": true,
            "description": "Your text saved in the draft for this language, or null."
          },
          "placeholders": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "first_name",
              "amount",
              "company",
              "card_link",
              "sender_name"
            ]
          },
          "required_placeholders": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "card_link"
            ]
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "en",
              "fr"
            ]
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "custom",
                "ai",
                "template"
              ]
            }
          }
        },
        "nullable": false
      },
      "InappState": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "failing_since",
          "access",
          "access_reason",
          "locale",
          "texts",
          "theme",
          "banner",
          "wall"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "inapp_state"
            ]
          },
          "customer": {
            "type": "string"
          },
          "failing_since": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "access": {
            "type": "string",
            "enum": [
              "full",
              "restricted"
            ],
            "description": "restricted exactly when wall.show is true: the same calculation as GET /v1/customers/{id}/access (DECISIONS 81, 87)."
          },
          "access_reason": {
            "type": "string",
            "enum": [
              "payment_failed",
              "paused",
              null
            ],
            "nullable": true,
            "description": "Why access is restricted; null when full. paused: only when the cancel flow's pause.inapp is wall."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr",
              "de",
              "it",
              "pt"
            ],
            "description": "?locale=, then the customer's language, then the account's."
          },
          "texts": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "string"
            },
            "description": "Banner and wall texts in this locale (banner_text, banner_cta, dismiss, banner_days_left, banner_last_day, wall_title, wall_text, wall_cta, pay_cta, amount_due, card_expired, decline_expired, decline_insufficient_funds, decline_authentication_required, decline_declined, checking_payment, back_in_title, back_in_text, data_safe, paused_banner_text, paused_title, paused_text, paused_resumes, resume_cta, resuming, resumed_title, resumed_text, cancel_now): the recovery flow's inapp.texts, else NoChurn's. {{amount}} is already filled (the unpaid amount, or the regular price when paused); {{days}}, {{date}} and {{exp}} are for the widget to fill (from details, days_left, in the customer's locale). Null when nothing is shown."
          },
          "theme": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CancelFlowTheme"
              }
            ],
            "nullable": true,
            "description": "The cancel flow's design (same tokens as the modal). Null when nothing is shown."
          },
          "banner": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "show"
            ],
            "properties": {
              "show": {
                "type": "boolean"
              },
              "text": {
                "type": "string"
              },
              "cta_url": {
                "type": "string",
                "nullable": true,
                "example": "https://pay.nochurn.sh/c/…"
              },
              "cta_label": {
                "type": "string",
                "nullable": true
              },
              "kind": {
                "type": "string",
                "enum": [
                  "payment_failed",
                  "paused"
                ]
              },
              "audience": {
                "type": "string",
                "enum": [
                  "admins",
                  "everyone"
                ]
              },
              "dismissible_for_hours": {
                "type": "integer"
              },
              "dismiss_label": {
                "type": "string"
              },
              "restricts_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "payment_failed: when the wall comes up if still unpaid (wall enabled, not up yet); else null."
              },
              "days_left": {
                "type": "integer",
                "nullable": true,
                "minimum": 0,
                "description": "Whole days until restricts_at, in the customer's time zone (0 = last day); null with restricts_at."
              },
              "details": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/InappPaymentDetails"
                  },
                  {
                    "$ref": "#/components/schemas/InappPausedDetails"
                  }
                ],
                "nullable": true,
                "description": "The facts the banner draws (same as wall.details); null when the banner is not shown."
              }
            }
          },
          "wall": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "show"
            ],
            "properties": {
              "show": {
                "type": "boolean"
              },
              "text": {
                "type": "string"
              },
              "cta_url": {
                "type": "string",
                "nullable": true,
                "example": "https://pay.nochurn.sh/c/…"
              },
              "cta_label": {
                "type": "string",
                "nullable": true
              },
              "kind": {
                "type": "string",
                "enum": [
                  "payment_failed",
                  "paused"
                ]
              },
              "shows_after_days": {
                "type": "integer",
                "nullable": true,
                "description": "payment_failed: the grace in days; null for paused (at once)."
              },
              "cancel": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "label",
                  "fallback_url"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "description": "The cancel flow's cancel_now text (or the in-app override)."
                  },
                  "fallback_url": {
                    "type": "string",
                    "nullable": true,
                    "description": "The publishable key's fallback cancel link (null for a secret key)."
                  }
                },
                "nullable": false,
                "description": "Click to cancel on the wall: open the cancel modal (NoChurn.cancel, POST /v1/cancel_sessions); if that fails, POST the customer token as a form field `token` to fallback_url."
              },
              "details": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/InappPaymentDetails"
                  },
                  {
                    "$ref": "#/components/schemas/InappPausedDetails"
                  }
                ],
                "nullable": true,
                "description": "The facts the wall draws; null when the wall is not shown. paused: cta_label is the resume button (POST /v1/inapp/resume), null when resume_allowed is false; cta_url is null."
              }
            }
          }
        },
        "nullable": false
      },
      "InappPaymentDetails": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "amount_due_cents",
          "currency",
          "invoice_due_at",
          "payment_method",
          "decline"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "payment_failed"
            ]
          },
          "amount_due_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "example": "eur"
          },
          "invoice_due_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The invoice's due date when the billing set one, else null."
          },
          "payment_method": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "brand",
              "last4",
              "exp_month",
              "exp_year"
            ],
            "properties": {
              "brand": {
                "type": "string",
                "nullable": true,
                "example": "visa"
              },
              "last4": {
                "type": "string",
                "nullable": true,
                "example": "4242"
              },
              "exp_month": {
                "type": "integer",
                "nullable": true
              },
              "exp_year": {
                "type": "integer",
                "nullable": true
              }
            },
            "nullable": true,
            "description": "The method of the failed payment, else the customer's default one; null when unknown."
          },
          "decline": {
            "type": "string",
            "enum": [
              "expired",
              "insufficient_funds",
              "authentication_required",
              "declined"
            ],
            "description": "Mapped from the provider's decline or failure code (expired_card → expired; insufficient_funds; authentication_required; anything else → declined)."
          }
        },
        "nullable": false,
        "description": "kind payment_failed: what the payment wall draws. Never more card data than brand, last4 and expiry; never the provider's message."
      },
      "InappPausedDetails": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "paused_at",
          "resumes_at",
          "regular_amount_cents",
          "currency",
          "resume_allowed"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "paused"
            ]
          },
          "paused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the pause started (null when NoChurn never saw it start)."
          },
          "resumes_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When it resumes by itself (null: no date set)."
          },
          "regular_amount_cents": {
            "type": "integer",
            "description": "The price per interval once resumed (quantity included)."
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "example": "eur"
          },
          "resume_allowed": {
            "type": "boolean",
            "description": "POST /v1/inapp/resume can resume it (Stripe, or a provider NoChurn resumes); false for billing by API."
          }
        },
        "nullable": false,
        "description": "kind paused: what the paused wall or banner draws."
      },
      "CancelSessionResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "currency",
          "regular_amount_cents",
          "next_invoice_at",
          "next_invoice_amount_cents",
          "discounted_until",
          "discounted_invoices",
          "resumes_at",
          "plan_name"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "discount",
              "pause",
              "downgrade",
              "talk"
            ]
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "example": "eur"
          },
          "regular_amount_cents": {
            "type": "integer",
            "nullable": true,
            "description": "The price per interval before the offer (quantity included). Null for talk."
          },
          "next_invoice_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "discount, downgrade: the next invoice's date (the provider's, else the renewal date)."
          },
          "next_invoice_amount_cents": {
            "type": "integer",
            "nullable": true,
            "description": "discount, downgrade: the next invoice's amount from the provider (Stripe: upcoming invoice preview, read once when the offer is applied). Null when the provider cannot tell."
          },
          "discounted_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "discount: when the usual price comes back (acceptance + the offer's months)."
          },
          "discounted_invoices": {
            "type": "array",
            "maxItems": 12,
            "description": "discount: the invoices due before discounted_until (12 at most), at next_invoice_amount_cents. [] otherwise.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "at",
                "amount_cents"
              ],
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "amount_cents": {
                  "type": "integer",
                  "nullable": true
                }
              },
              "nullable": false
            }
          },
          "resumes_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "pause: the resume date (null when your billing pauses it, billing by API)."
          },
          "plan_name": {
            "type": "string",
            "nullable": true,
            "description": "downgrade: the new plan, when the offer names it."
          }
        },
        "nullable": false,
        "description": "The end screen of a saved customer. Never an invented number: unknown = null."
      },
      "CustomerAccess": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "livemode",
          "access",
          "reason",
          "since",
          "failing_since",
          "restricts_at",
          "invoice",
          "pay_url",
          "subscription",
          "paused_at",
          "resumes_at",
          "cancel_url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_access"
            ]
          },
          "customer": {
            "type": "string",
            "example": "cst_8Hd2kQ9xLm3VbN7p"
          },
          "livemode": {
            "type": "boolean"
          },
          "access": {
            "type": "string",
            "enum": [
              "full",
              "restricted"
            ],
            "description": "restricted: block your app (keep paying, resuming and cancelling reachable)."
          },
          "reason": {
            "type": "string",
            "enum": [
              "payment_failed",
              "paused",
              null
            ],
            "nullable": true,
            "description": "Null when full. paused: every live subscription is paused and the cancel flow's pause.inapp is wall."
          },
          "since": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the restriction started (the failure + the grace, or the start of the pause; null when unknown). Null when full."
          },
          "failing_since": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the oldest unpaid invoice under recovery started failing."
          },
          "restricts_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When access will be restricted if still unpaid (full now, wall enabled); else null."
          },
          "invoice": {
            "type": "string",
            "nullable": true,
            "description": "The unpaid invoice (ninv_…) that sets the clock."
          },
          "pay_url": {
            "type": "string",
            "nullable": true,
            "description": "Where the customer pays or updates their card (null when nothing is failing)."
          },
          "subscription": {
            "type": "string",
            "nullable": true,
            "description": "The paused subscription (nsub_…) when the cancel flow shows the pause (banner or wall); else null."
          },
          "paused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "With subscription: when the pause started (null when unknown)."
          },
          "resumes_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "With subscription: when it resumes by itself (null: no date)."
          },
          "cancel_url": {
            "type": "string",
            "nullable": true,
            "description": "Where the customer cancels in one click (cancel.nochurn.sh/a/…, valid 30 days): show it next to pay_url (click to cancel). Null when nothing is failing or paused."
          }
        },
        "nullable": false,
        "description": "May this customer use your app? Same calculation as the in-app wall (wall.after_days, annual_grace_days, wall.enabled of the recovery flow live in this mode; pause.inapp of the cancel flow). Holdout customers, paused flows, a disabled wall or no recovery run: full. Never card data."
      },
      "WidgetSecret": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "configured",
          "created_at",
          "secret"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "widget_secret"
            ]
          },
          "configured": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "secret": {
            "type": "string",
            "nullable": true,
            "description": "Only when created or rotated: shown once."
          }
        },
        "nullable": false
      },
      "WebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "url",
          "description",
          "events",
          "status",
          "secret",
          "last_status",
          "last_delivery_at",
          "failure_streak",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "webhook_endpoint"
            ]
          },
          "id": {
            "type": "string",
            "example": "whk_7Hd2kQ9xLm3VbN7p"
          },
          "livemode": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ]
          },
          "secret": {
            "type": "string",
            "nullable": true,
            "description": "whsec_…: only when created or rotated."
          },
          "last_status": {
            "type": "integer",
            "nullable": true
          },
          "last_delivery_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "failure_streak": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "WebhookDelivery": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "endpoint",
          "destination",
          "event",
          "type",
          "status",
          "attempts",
          "next_attempt_at",
          "response_status",
          "response_ms",
          "error",
          "delivered_at",
          "failed_at",
          "replay_of",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "webhook_delivery"
            ]
          },
          "id": {
            "type": "string",
            "example": "whd_2Kd9xLm3VbN7pQ4s"
          },
          "livemode": {
            "type": "boolean"
          },
          "endpoint": {
            "type": "string"
          },
          "destination": {
            "type": "string",
            "enum": [
              "http",
              "slack",
              "intercom_note"
            ],
            "description": "http: your endpoint; slack / intercom_note: a flow notification."
          },
          "event": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "retrying",
              "delivered",
              "failed",
              "skipped"
            ],
            "description": "skipped: a notification NoChurn chose not to send (no Intercom contact for this customer)."
          },
          "attempts": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "response_status": {
            "type": "integer",
            "nullable": true
          },
          "response_ms": {
            "type": "integer",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "failed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "replay_of": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "WebhookDeliveryDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "endpoint",
          "destination",
          "event",
          "type",
          "status",
          "attempts",
          "next_attempt_at",
          "response_status",
          "response_ms",
          "error",
          "delivered_at",
          "failed_at",
          "replay_of",
          "created_at",
          "payload",
          "response_excerpt"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "webhook_delivery"
            ]
          },
          "id": {
            "type": "string",
            "example": "whd_2Kd9xLm3VbN7pQ4s"
          },
          "livemode": {
            "type": "boolean"
          },
          "endpoint": {
            "type": "string"
          },
          "destination": {
            "type": "string",
            "enum": [
              "http",
              "slack",
              "intercom_note"
            ],
            "description": "http: your endpoint; slack / intercom_note: a flow notification."
          },
          "event": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "retrying",
              "delivered",
              "failed",
              "skipped"
            ],
            "description": "skipped: a notification NoChurn chose not to send (no Intercom contact for this customer)."
          },
          "attempts": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "response_status": {
            "type": "integer",
            "nullable": true
          },
          "response_ms": {
            "type": "integer",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "failed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "replay_of": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "payload": {
            "type": "object",
            "additionalProperties": true
          },
          "response_excerpt": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": false
      },
      "Payment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "customer",
          "subscription",
          "invoice",
          "external_id",
          "stripe_charge_id",
          "status",
          "amount_cents",
          "currency",
          "decline_code",
          "failure_code",
          "occurred_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "payment"
            ]
          },
          "id": {
            "type": "string",
            "example": "npay_4Hs8dK2mQx7VbN4p"
          },
          "livemode": {
            "type": "boolean"
          },
          "customer": {
            "type": "string"
          },
          "subscription": {
            "type": "string",
            "nullable": true
          },
          "invoice": {
            "type": "string",
            "nullable": true
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "stripe_charge_id": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed",
              "pending"
            ]
          },
          "amount_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "decline_code": {
            "type": "string",
            "nullable": true
          },
          "failure_code": {
            "type": "string",
            "nullable": true
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Offer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "kind",
          "name",
          "params",
          "archived_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "offer"
            ]
          },
          "id": {
            "type": "string",
            "example": "off_8Kd2mX7pQs4LzA1b"
          },
          "kind": {
            "type": "string",
            "enum": [
              "discount",
              "pause",
              "downgrade",
              "talk"
            ]
          },
          "name": {
            "type": "string",
            "example": "30% for 3 months"
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "example": {
              "percent": 30,
              "months": 3
            }
          },
          "archived_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "CancelSession": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "step",
          "outcome",
          "locale",
          "holdout",
          "published",
          "customer",
          "subscription",
          "subscriptions",
          "reasons",
          "offer",
          "talk_url",
          "texts",
          "feedback_enabled",
          "feedback",
          "theme",
          "cancel_url_now",
          "cancel_status",
          "cancel_effective_at",
          "result",
          "access_until",
          "confirmation_email_sent",
          "review",
          "expires_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cancel_session"
            ]
          },
          "id": {
            "type": "string",
            "example": "ses_3Ls9dK2mQx7VbN4p"
          },
          "livemode": {
            "type": "boolean"
          },
          "step": {
            "type": "string",
            "enum": [
              "choose_subscription",
              "reason",
              "offer",
              "feedback",
              "confirm",
              "done"
            ]
          },
          "outcome": {
            "type": "string",
            "nullable": true,
            "enum": [
              "saved",
              "cancelled",
              "abandoned",
              null
            ]
          },
          "locale": {
            "type": "string"
          },
          "holdout": {
            "type": "boolean"
          },
          "published": {
            "type": "boolean"
          },
          "customer": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          },
          "subscription": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "plan",
              "status",
              "amount_cents",
              "currency",
              "interval",
              "interval_count",
              "current_period_end",
              "cancel_at_period_end"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "nsub_7Hd2kQ9xLm3VbN4p"
              },
              "plan": {
                "type": "string",
                "nullable": true
              },
              "status": {
                "type": "string",
                "enum": [
                  "incomplete",
                  "incomplete_expired",
                  "trialing",
                  "active",
                  "past_due",
                  "canceled",
                  "unpaid",
                  "paused"
                ]
              },
              "amount_cents": {
                "type": "integer",
                "description": "Price per interval (quantity included), minor unit."
              },
              "currency": {
                "type": "string",
                "nullable": true
              },
              "interval": {
                "type": "string",
                "nullable": true,
                "example": "month"
              },
              "interval_count": {
                "type": "integer"
              },
              "current_period_end": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Next renewal."
              },
              "cancel_at_period_end": {
                "type": "boolean"
              }
            },
            "nullable": true,
            "description": "The subscription this session targets (offers, saved MRR, cancellation). Null at choose_subscription, for an unknown customer, or without a live subscription."
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "plan",
                "status",
                "amount_cents",
                "currency",
                "interval",
                "interval_count",
                "current_period_end",
                "cancel_at_period_end"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "nsub_7Hd2kQ9xLm3VbN4p"
                },
                "plan": {
                  "type": "string",
                  "nullable": true
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "incomplete",
                    "incomplete_expired",
                    "trialing",
                    "active",
                    "past_due",
                    "canceled",
                    "unpaid",
                    "paused"
                  ]
                },
                "amount_cents": {
                  "type": "integer",
                  "description": "Price per interval (quantity included), minor unit."
                },
                "currency": {
                  "type": "string",
                  "nullable": true
                },
                "interval": {
                  "type": "string",
                  "nullable": true,
                  "example": "month"
                },
                "interval_count": {
                  "type": "integer"
                },
                "current_period_end": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true,
                  "description": "Next renewal."
                },
                "cancel_at_period_end": {
                  "type": "boolean"
                }
              },
              "nullable": false
            },
            "description": "At choose_subscription only (several live subscriptions, none named by the token's `subscription` claim): the customer picks one (answer { subscription }) or cancels one (cancel { subscription }). Empty otherwise."
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "label",
                "free_text"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "too_expensive"
                },
                "label": {
                  "type": "string"
                },
                "free_text": {
                  "type": "boolean"
                }
              },
              "nullable": false
            }
          },
          "offer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "kind",
              "title",
              "url",
              "percent",
              "months",
              "plan_name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "discount",
                  "pause",
                  "downgrade",
                  "talk"
                ]
              },
              "title": {
                "type": "string",
                "description": "In the session's language, with the account's text for that offer kind (texts.<lang>.offer_<kind>) when set."
              },
              "url": {
                "type": "string",
                "nullable": true,
                "description": "talk: the booking link."
              },
              "percent": {
                "type": "integer",
                "nullable": true,
                "description": "discount: percent off."
              },
              "months": {
                "type": "integer",
                "nullable": true,
                "description": "discount: how many months; pause: how long."
              },
              "plan_name": {
                "type": "string",
                "nullable": true,
                "description": "downgrade: the target plan's name, when the offer has one."
              }
            },
            "nullable": true
          },
          "talk_url": {
            "type": "string",
            "nullable": true
          },
          "texts": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "feedback_enabled": {
            "type": "boolean",
            "description": "Same as feedback.enabled (kept for older widgets)."
          },
          "feedback": {
            "$ref": "#/components/schemas/CancelFeedback"
          },
          "theme": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CancelFlowTheme"
              }
            ],
            "description": "This session's design: the flow's, or its segment's / A/B variant's."
          },
          "cancel_url_now": {
            "type": "string",
            "nullable": true,
            "description": "Shown once, when the session is created: cancels at any step."
          },
          "cancel_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "done",
              "requested",
              "failed",
              null
            ]
          },
          "cancel_effective_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "result": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CancelSessionResult"
              }
            ],
            "nullable": true,
            "description": "Once an offer is accepted: outcome saved (discount, pause, downgrade), or a talk offer accepted (the session goes on). Null otherwise."
          },
          "access_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Cancelled: access lasts until then (cancel_effective_at: the period end, or now when immediate). Null otherwise, and while the cancellation is pending."
          },
          "confirmation_email_sent": {
            "type": "boolean",
            "description": "A cancellation confirmation email was actually queued. NoChurn sends none today: always false (the widget hides that fact)."
          },
          "review": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "platform",
              "url",
              "text",
              "cta"
            ],
            "properties": {
              "platform": {
                "type": "string",
                "enum": [
                  "g2",
                  "capterra",
                  "trustpilot",
                  "google",
                  "app_store",
                  "product_hunt"
                ]
              },
              "url": {
                "type": "string"
              },
              "text": {
                "type": "string"
              },
              "cta": {
                "type": "string"
              }
            },
            "nullable": true,
            "description": "Asked on the last step to happy leavers only, once per customer, never tied to an offer."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "CancelFlow": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "published",
          "version",
          "locale",
          "texts",
          "reasons",
          "feedback_enabled",
          "feedback",
          "theme",
          "languages",
          "cancel_fallback_url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cancel_flow"
            ]
          },
          "id": {
            "type": "string"
          },
          "published": {
            "type": "boolean"
          },
          "version": {
            "type": "integer",
            "nullable": true
          },
          "locale": {
            "type": "string"
          },
          "texts": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "label",
                "free_text"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "too_expensive"
                },
                "label": {
                  "type": "string"
                },
                "free_text": {
                  "type": "boolean"
                }
              },
              "nullable": false
            }
          },
          "feedback_enabled": {
            "type": "boolean"
          },
          "feedback": {
            "$ref": "#/components/schemas/CancelFeedback"
          },
          "theme": {
            "$ref": "#/components/schemas/CancelFlowTheme"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cancel_fallback_url": {
            "type": "string",
            "nullable": true,
            "description": "With a publishable key: the fallback cancel link (POST { token } as a form). Null for a secret key."
          }
        },
        "nullable": false
      },
      "CancelFlowTheme": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "preset",
          "format",
          "tokens",
          "font_url",
          "brand",
          "logo_url",
          "app_name",
          "end_screen",
          "custom_css",
          "cancel_button_check",
          "classes"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cancel_flow_theme"
            ]
          },
          "preset": {
            "type": "string",
            "enum": [
              "light",
              "dark",
              "playful",
              "custom_css",
              "headless"
            ]
          },
          "format": {
            "type": "string",
            "enum": [
              "modal",
              "panel",
              "page"
            ],
            "description": "modal (centred), panel (side panel) or page (full page)."
          },
          "tokens": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "bg",
              "surface",
              "text",
              "muted",
              "accent",
              "accent_text",
              "border",
              "danger",
              "font",
              "font_size",
              "radius",
              "radius_control",
              "overlay",
              "max_width",
              "shadow",
              "spacing",
              "border_width",
              "border_style",
              "font_scale",
              "font_weight_title"
            ],
            "properties": {
              "bg": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "surface": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "text": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "muted": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "accent": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "accent_text": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "border": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "danger": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$"
              },
              "font": {
                "type": "string",
                "maxLength": 200,
                "example": "Inter, system-ui, sans-serif",
                "description": "A font stack. Its first family loads from font_url when it is an allowed Google Font."
              },
              "font_size": {
                "type": "number",
                "minimum": 12,
                "maximum": 20,
                "description": "Base size, px."
              },
              "radius": {
                "type": "integer",
                "minimum": 0,
                "maximum": 999,
                "description": "Modal corners, px."
              },
              "radius_control": {
                "type": "integer",
                "minimum": 0,
                "maximum": 999,
                "description": "Buttons and fields, px."
              },
              "overlay": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "description": "Backdrop opacity."
              },
              "max_width": {
                "type": "integer",
                "minimum": 280,
                "maximum": 1200,
                "description": "Modal width, px."
              },
              "shadow": {
                "type": "string",
                "enum": [
                  "none",
                  "sm",
                  "md",
                  "lg"
                ]
              },
              "spacing": {
                "type": "string",
                "enum": [
                  "compact",
                  "regular",
                  "comfortable"
                ],
                "description": "Density."
              },
              "border_width": {
                "type": "integer",
                "minimum": 0,
                "maximum": 4,
                "description": "px."
              },
              "border_style": {
                "type": "string",
                "enum": [
                  "solid",
                  "dashed",
                  "none"
                ]
              },
              "font_scale": {
                "type": "number",
                "minimum": 1,
                "maximum": 2,
                "description": "Title size = font_size × font_scale."
              },
              "font_weight_title": {
                "type": "integer",
                "enum": [
                  400,
                  500,
                  600,
                  700
                ]
              }
            },
            "nullable": false,
            "description": "The --nc-* custom properties (--nc-accent-text for accent_text). Enumerated values are mapped to CSS by the widget."
          },
          "font_url": {
            "type": "string",
            "nullable": true,
            "description": "Google Fonts stylesheet of the font token's first family (allowed fonts only), else null."
          },
          "brand": {
            "type": "string",
            "enum": [
              "logo",
              "name",
              "none"
            ],
            "description": "What the header shows: logo (logo_url, else app_name), name (app_name) or none (nothing). logo_url and app_name already follow it: null when the header must not show them."
          },
          "logo_url": {
            "type": "string",
            "nullable": true,
            "description": "https URL of the logo (uploaded, or set in the editor) when brand is logo, else null (show app_name)."
          },
          "app_name": {
            "type": "string",
            "nullable": true,
            "description": "The account's name: the text shown in place of a logo; null when brand is none."
          },
          "end_screen": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "style",
              "auto_close_seconds"
            ],
            "properties": {
              "style": {
                "type": "string",
                "enum": [
                  "celebrate",
                  "glow",
                  "quiet"
                ],
                "description": "Saved customer: celebrate (default), glow or quiet."
              },
              "auto_close_seconds": {
                "type": "integer",
                "nullable": true,
                "minimum": 3,
                "maximum": 30,
                "description": "Saved screen only: closes by itself after this (default 6); null = never."
              }
            },
            "nullable": false,
            "description": "The modal's last step (draft.design.end_screen). A cancelled customer's end screen is always calm, whatever the style."
          },
          "custom_css": {
            "type": "string",
            "description": "Cleaned on the server (no @import, url() only to Google Fonts)."
          },
          "cancel_button_check": {
            "type": "string",
            "enum": [
              "ok",
              "fallback_applied"
            ]
          },
          "classes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "nc-modal",
                "nc-title",
                "nc-reason",
                "nc-offer",
                "nc-btn-primary",
                "nc-btn-cancel",
                "nc-footer"
              ]
            }
          }
        },
        "nullable": false
      },
      "CancelFlowLogo": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "flow",
          "url",
          "content_type",
          "byte_size",
          "width",
          "height",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cancel_flow_logo"
            ]
          },
          "id": {
            "type": "string",
            "example": "clg_3Ls9dK2mQx7VbN4p"
          },
          "flow": {
            "type": "string",
            "example": "flw_3Ls9dK2mQx7VbN4p"
          },
          "url": {
            "type": "string",
            "example": "https://api.nochurn.sh/assets/logos/3Ls9dK2mQx7VbN4pZ8rT1yWq5eHn6uJc.png",
            "description": "Public and stable: put it in draft.design.logo_url. No account id in it; served without auth."
          },
          "content_type": {
            "type": "string",
            "enum": [
              "image/png",
              "image/jpeg",
              "image/webp"
            ]
          },
          "byte_size": {
            "type": "integer"
          },
          "width": {
            "type": "integer",
            "description": "px"
          },
          "height": {
            "type": "integer",
            "description": "px"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "CancelFeedback": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "enabled",
          "required"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "The feedback step is shown."
          },
          "required": {
            "type": "boolean",
            "description": "\"Continue\" needs a text (400 otherwise). Cancelling never does."
          }
        },
        "nullable": false
      },
      "WidgetPing": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "detected_at",
          "cancel_fallback_url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "widget_ping"
            ]
          },
          "detected_at": {
            "type": "string",
            "format": "date-time"
          },
          "cancel_fallback_url": {
            "type": "string",
            "nullable": true,
            "description": "The key's fallback cancel link: POST { token } as a form when a cancel session cannot be opened."
          }
        },
        "nullable": false
      },
      "WidgetGuardReport": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "recorded",
          "event"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "widget_guard_report"
            ]
          },
          "recorded": {
            "type": "boolean",
            "description": "false: the same alert was recorded less than an hour ago."
          },
          "event": {
            "type": "string",
            "nullable": true,
            "description": "The Event (nevt_) recorded."
          }
        },
        "nullable": false
      },
      "ReviewPlatform": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "kind",
          "url",
          "status",
          "review_count",
          "rating",
          "rules",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "review_platform"
            ]
          },
          "id": {
            "type": "string",
            "example": "rvp_5Hd2kQ9xLm3VbN7p"
          },
          "kind": {
            "type": "string",
            "enum": [
              "g2",
              "capterra",
              "trustpilot",
              "google",
              "app_store",
              "product_hunt"
            ]
          },
          "url": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "review_count": {
            "type": "integer",
            "nullable": true
          },
          "rating": {
            "type": "number",
            "nullable": true
          },
          "rules": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "ReviewRequestStats": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "period_start",
          "cancellations",
          "requested",
          "clicked",
          "follow_ups_sent",
          "by_platform"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "review_request_stats"
            ]
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "cancellations": {
            "type": "integer"
          },
          "requested": {
            "type": "integer"
          },
          "clicked": {
            "type": "integer"
          },
          "follow_ups_sent": {
            "type": "integer"
          },
          "by_platform": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        },
        "nullable": false
      },
      "SendScheduleFlowOverride": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "flow",
          "version",
          "published",
          "draft",
          "segments",
          "variants"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "send_schedule_flow_override"
            ]
          },
          "flow": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "kind",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "flw_3Ls9dK2mQx7VbN4p"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "prevention",
                  "recovery",
                  "cancel",
                  "winback"
                ]
              },
              "name": {
                "type": "string"
              }
            },
            "nullable": false
          },
          "version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "number",
              "published_to"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "flv_9Kd2mX7pQs4LzA1b"
              },
              "number": {
                "type": "integer"
              },
              "published_to": {
                "type": "string",
                "enum": [
                  "test",
                  "live"
                ]
              }
            },
            "nullable": true,
            "description": "The version that runs in this mode; null when none."
          },
          "published": {
            "type": "object",
            "additionalProperties": true,
            "description": "Options the version of this mode sets itself.",
            "properties": {
              "audience": {
                "type": "string",
                "enum": [
                  "b2b",
                  "b2c"
                ]
              },
              "respect_customer_timezone": {
                "type": "boolean"
              },
              "skip_weekends": {
                "type": "boolean"
              },
              "skip_public_holidays": {
                "type": "boolean",
                "description": "Holidays of the customer's country (and state)."
              },
              "windows": {
                "type": "object",
                "properties": {
                  "weekday": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "weekend": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "quiet_hours": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "days_off": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "date"
                }
              }
            }
          },
          "draft": {
            "type": "object",
            "additionalProperties": true,
            "description": "Options the draft sets itself (published at the next publication).",
            "properties": {
              "audience": {
                "type": "string",
                "enum": [
                  "b2b",
                  "b2c"
                ]
              },
              "respect_customer_timezone": {
                "type": "boolean"
              },
              "skip_weekends": {
                "type": "boolean"
              },
              "skip_public_holidays": {
                "type": "boolean",
                "description": "Holidays of the customer's country (and state)."
              },
              "windows": {
                "type": "object",
                "properties": {
                  "weekday": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "weekend": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "quiet_hours": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "days_off": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "date"
                }
              }
            }
          },
          "segments": {
            "type": "array",
            "description": "Segments of the flow whose THEN sets options itself. A segment has no mode and no draft: it applies to the next runs of both modes.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "name",
                "is_default",
                "overrides"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "seg_4Jd8sK2mQx7VbN4p"
                },
                "name": {
                  "type": "string"
                },
                "is_default": {
                  "type": "boolean",
                  "description": "Everyone else."
                },
                "overrides": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Options the segment sets itself.",
                  "properties": {
                    "audience": {
                      "type": "string",
                      "enum": [
                        "b2b",
                        "b2c"
                      ]
                    },
                    "respect_customer_timezone": {
                      "type": "boolean"
                    },
                    "skip_weekends": {
                      "type": "boolean"
                    },
                    "skip_public_holidays": {
                      "type": "boolean",
                      "description": "Holidays of the customer's country (and state)."
                    },
                    "windows": {
                      "type": "object",
                      "properties": {
                        "weekday": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "weekend": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "quiet_hours": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "from": {
                          "type": "string"
                        },
                        "to": {
                          "type": "string"
                        }
                      }
                    },
                    "days_off": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "date"
                      }
                    }
                  }
                }
              },
              "nullable": false
            }
          },
          "variants": {
            "type": "array",
            "description": "A/B variants of the flow that set options themselves: every variant of a draft or running test, the winner of a decided test (stopped tests and losing variants never apply again). No mode. Those of a running test are listed with applies false: applying does not change them.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "label",
                "is_control",
                "experiment",
                "overrides",
                "applies",
                "not_applied_reason"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "abv_7Hs2dK9mQx4VbN1p"
                },
                "label": {
                  "type": "string",
                  "enum": [
                    "A",
                    "B",
                    "C",
                    "D"
                  ]
                },
                "is_control": {
                  "type": "boolean"
                },
                "experiment": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "name",
                    "status"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "abt_2Kd9sL4mQx7VbN3p"
                    },
                    "name": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "draft",
                        "running",
                        "decided"
                      ],
                      "description": "draft: not started, the variant applies once it starts."
                    }
                  },
                  "nullable": false
                },
                "overrides": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Options the variant sets itself.",
                  "properties": {
                    "audience": {
                      "type": "string",
                      "enum": [
                        "b2b",
                        "b2c"
                      ]
                    },
                    "respect_customer_timezone": {
                      "type": "boolean"
                    },
                    "skip_weekends": {
                      "type": "boolean"
                    },
                    "skip_public_holidays": {
                      "type": "boolean",
                      "description": "Holidays of the customer's country (and state)."
                    },
                    "windows": {
                      "type": "object",
                      "properties": {
                        "weekday": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "weekend": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "quiet_hours": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "from": {
                          "type": "string"
                        },
                        "to": {
                          "type": "string"
                        }
                      }
                    },
                    "days_off": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "date"
                      }
                    }
                  }
                },
                "applies": {
                  "type": "boolean",
                  "description": "false: the variant's test is running; applying leaves it unchanged (stop the test to apply)."
                },
                "not_applied_reason": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "running_test",
                    null
                  ],
                  "description": "Why applying does not change the variant; null when it does."
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "SendScheduleFlowsApplied": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "applied",
          "counts",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "send_schedule_flows_applied"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "applied": {
            "type": "integer",
            "description": "Number of flows changed, in their own settings, segments or A/B variants (0 when none overrode the account)."
          },
          "counts": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "flows",
              "segments",
              "variants"
            ],
            "properties": {
              "flows": {
                "type": "integer",
                "description": "Flows whose own settings changed (published version of this mode or draft)."
              },
              "segments": {
                "type": "integer"
              },
              "variants": {
                "type": "integer"
              }
            },
            "nullable": false
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "object",
                "flow",
                "version",
                "published",
                "draft",
                "segments",
                "variants"
              ],
              "properties": {
                "object": {
                  "type": "string",
                  "enum": [
                    "send_schedule_flow_applied"
                  ]
                },
                "flow": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "kind",
                    "name"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "flw_3Ls9dK2mQx7VbN4p"
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "prevention",
                        "recovery",
                        "cancel",
                        "winback"
                      ]
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "nullable": false
                },
                "version": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "number",
                    "published_to"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "flv_9Kd2mX7pQs4LzA1b"
                    },
                    "number": {
                      "type": "integer"
                    },
                    "published_to": {
                      "type": "string",
                      "enum": [
                        "test",
                        "live"
                      ]
                    }
                  },
                  "nullable": true,
                  "description": "The version published with those options at null; null when only the draft changed."
                },
                "published": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "The options the replaced version set (now null).",
                  "properties": {
                    "audience": {
                      "type": "string",
                      "enum": [
                        "b2b",
                        "b2c"
                      ]
                    },
                    "respect_customer_timezone": {
                      "type": "boolean"
                    },
                    "skip_weekends": {
                      "type": "boolean"
                    },
                    "skip_public_holidays": {
                      "type": "boolean",
                      "description": "Holidays of the customer's country (and state)."
                    },
                    "windows": {
                      "type": "object",
                      "properties": {
                        "weekday": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "weekend": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "quiet_hours": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "from": {
                          "type": "string"
                        },
                        "to": {
                          "type": "string"
                        }
                      }
                    },
                    "days_off": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "date"
                      }
                    }
                  }
                },
                "draft": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "The options the draft set (now null).",
                  "properties": {
                    "audience": {
                      "type": "string",
                      "enum": [
                        "b2b",
                        "b2c"
                      ]
                    },
                    "respect_customer_timezone": {
                      "type": "boolean"
                    },
                    "skip_weekends": {
                      "type": "boolean"
                    },
                    "skip_public_holidays": {
                      "type": "boolean",
                      "description": "Holidays of the customer's country (and state)."
                    },
                    "windows": {
                      "type": "object",
                      "properties": {
                        "weekday": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "weekend": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "quiet_hours": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "from": {
                          "type": "string"
                        },
                        "to": {
                          "type": "string"
                        }
                      }
                    },
                    "days_off": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "date"
                      }
                    }
                  }
                },
                "segments": {
                  "type": "array",
                  "description": "The segments whose THEN no longer sets those options.",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "id",
                      "name",
                      "is_default",
                      "overrides"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "seg_4Jd8sK2mQx7VbN4p"
                      },
                      "name": {
                        "type": "string"
                      },
                      "is_default": {
                        "type": "boolean",
                        "description": "Everyone else."
                      },
                      "overrides": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The options the segment set (now removed: it follows its flow).",
                        "properties": {
                          "audience": {
                            "type": "string",
                            "enum": [
                              "b2b",
                              "b2c"
                            ]
                          },
                          "respect_customer_timezone": {
                            "type": "boolean"
                          },
                          "skip_weekends": {
                            "type": "boolean"
                          },
                          "skip_public_holidays": {
                            "type": "boolean",
                            "description": "Holidays of the customer's country (and state)."
                          },
                          "windows": {
                            "type": "object",
                            "properties": {
                              "weekday": {
                                "type": "array",
                                "items": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                }
                              },
                              "weekend": {
                                "type": "array",
                                "items": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          },
                          "quiet_hours": {
                            "type": "object",
                            "properties": {
                              "enabled": {
                                "type": "boolean"
                              },
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string"
                              }
                            }
                          },
                          "days_off": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "date"
                            }
                          }
                        }
                      }
                    },
                    "nullable": false
                  }
                },
                "variants": {
                  "type": "array",
                  "description": "The A/B variants that no longer set those options (never those of a running test).",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "id",
                      "label",
                      "is_control",
                      "experiment",
                      "overrides",
                      "applies",
                      "not_applied_reason"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "abv_7Hs2dK9mQx4VbN1p"
                      },
                      "label": {
                        "type": "string",
                        "enum": [
                          "A",
                          "B",
                          "C",
                          "D"
                        ]
                      },
                      "is_control": {
                        "type": "boolean"
                      },
                      "experiment": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "id",
                          "name",
                          "status"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "abt_2Kd9sL4mQx7VbN3p"
                          },
                          "name": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "draft",
                              "running",
                              "decided"
                            ],
                            "description": "draft: not started, the variant applies once it starts."
                          }
                        },
                        "nullable": false
                      },
                      "overrides": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The options the variant set (now removed: it follows its flow).",
                        "properties": {
                          "audience": {
                            "type": "string",
                            "enum": [
                              "b2b",
                              "b2c"
                            ]
                          },
                          "respect_customer_timezone": {
                            "type": "boolean"
                          },
                          "skip_weekends": {
                            "type": "boolean"
                          },
                          "skip_public_holidays": {
                            "type": "boolean",
                            "description": "Holidays of the customer's country (and state)."
                          },
                          "windows": {
                            "type": "object",
                            "properties": {
                              "weekday": {
                                "type": "array",
                                "items": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                }
                              },
                              "weekend": {
                                "type": "array",
                                "items": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          },
                          "quiet_hours": {
                            "type": "object",
                            "properties": {
                              "enabled": {
                                "type": "boolean"
                              },
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string"
                              }
                            }
                          },
                          "days_off": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "date"
                            }
                          }
                        }
                      },
                      "applies": {
                        "type": "boolean",
                        "description": "false: the variant's test is running; applying leaves it unchanged (stop the test to apply)."
                      },
                      "not_applied_reason": {
                        "type": "string",
                        "nullable": true,
                        "enum": [
                          "running_test",
                          null
                        ],
                        "description": "Why applying does not change the variant; null when it does."
                      }
                    },
                    "nullable": false
                  }
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "ChurnScore": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "score",
          "level",
          "model_version",
          "computed_at",
          "factors",
          "flag",
          "history"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "churn_score"
            ]
          },
          "customer": {
            "type": "string",
            "example": "cst_4fJ8sK2pQ9wLm3Xz"
          },
          "score": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 100,
            "description": "null until computed."
          },
          "level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              null
            ],
            "nullable": true,
            "description": "Thresholds of the prevention flow (high ≥ 70, medium ≥ 40 by default)."
          },
          "model_version": {
            "type": "string",
            "nullable": true,
            "example": "heuristic-v1"
          },
          "computed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "factors": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "label",
                "points"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "payment_method"
                },
                "label": {
                  "type": "string",
                  "example": "Prepaid card"
                },
                "points": {
                  "type": "integer",
                  "example": 65
                }
              },
              "nullable": false
            },
            "description": "What explains the score, points added (or removed)."
          },
          "flag": {
            "$ref": "#/components/schemas/CustomerFlag"
          },
          "history": {
            "type": "array",
            "description": "Last 30 computations, newest first.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "score",
                "level",
                "trigger",
                "computed_at"
              ],
              "properties": {
                "score": {
                  "type": "integer"
                },
                "level": {
                  "type": "string",
                  "enum": [
                    "low",
                    "medium",
                    "high"
                  ]
                },
                "trigger": {
                  "type": "string",
                  "enum": [
                    "signup",
                    "daily",
                    "signal",
                    "sync",
                    "manual",
                    "attributes",
                    "cancel_session",
                    "import"
                  ]
                },
                "computed_at": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "CustomerFlag": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reason",
          "flagged_at"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "enum": [
              "card_removed",
              "card_expiring"
            ]
          },
          "flagged_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": true,
        "description": "FLAGGED: a prevention signal before the renewal. Cleared when a new card arrives."
      },
      "AttributeDefinition": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "key",
          "type",
          "sensitive",
          "customers_count",
          "coverage",
          "last_seen_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "attribute_definition"
            ]
          },
          "id": {
            "type": "string",
            "example": "atd_7Hq2LmX9pK3sNd4v"
          },
          "key": {
            "type": "string",
            "example": "signup_source"
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean",
              "date"
            ],
            "description": "Inferred at the first send, then locked."
          },
          "sensitive": {
            "type": "boolean",
            "description": "Never sent to the AI."
          },
          "customers_count": {
            "type": "integer",
            "description": "Customers of this mode with a value."
          },
          "coverage": {
            "type": "number",
            "nullable": true,
            "description": "customers_count / customers of this mode; null without customers."
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Import": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "status",
          "filename",
          "mapping",
          "rows_total",
          "rows_imported",
          "rows_failed",
          "errors",
          "livemode",
          "created_at",
          "started_at",
          "finished_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "import"
            ]
          },
          "id": {
            "type": "string",
            "example": "imp_3Kd9sL2mQx7VbN4p"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "completed",
              "failed"
            ]
          },
          "filename": {
            "type": "string",
            "nullable": true,
            "example": "customers.csv"
          },
          "mapping": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "User ID": "external_id",
              "Plan": "attribute:plan"
            }
          },
          "rows_total": {
            "type": "integer"
          },
          "rows_imported": {
            "type": "integer"
          },
          "rows_failed": {
            "type": "integer"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "line",
                "code",
                "message"
              ],
              "properties": {
                "line": {
                  "type": "integer",
                  "description": "Line in the file (1 = header)."
                },
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "nullable": false
            },
            "description": "Row by row, 500 at most."
          },
          "livemode": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "nullable": false
      },
      "ImportPreview": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "columns",
          "rows_total",
          "rows"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "import_preview"
            ]
          },
          "columns": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "target"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "target": {
                  "type": "string",
                  "example": "attribute:plan"
                }
              },
              "nullable": false
            }
          },
          "rows_total": {
            "type": "integer"
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": {
                "type": "string",
                "nullable": true
              }
            },
            "description": "First 10 rows."
          }
        },
        "nullable": false
      },
      "MessageReply": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "message",
          "customer",
          "customer_email",
          "customer_name",
          "flow_run",
          "flow",
          "from",
          "subject",
          "text",
          "category",
          "summary",
          "needs_human",
          "status",
          "handled_at",
          "answer_message",
          "received_at",
          "livemode"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "message_reply"
            ]
          },
          "id": {
            "type": "string",
            "example": "rpl_8Kq2mX7pLs4NzA1b"
          },
          "message": {
            "type": "string",
            "description": "The email it answers (msg_…)."
          },
          "customer": {
            "type": "string",
            "nullable": true
          },
          "customer_email": {
            "type": "string",
            "nullable": true
          },
          "customer_name": {
            "type": "string",
            "nullable": true
          },
          "flow_run": {
            "type": "string",
            "nullable": true
          },
          "flow": {
            "type": "string",
            "nullable": true,
            "enum": [
              "prevention",
              "recovery",
              "cancel",
              "winback",
              null
            ]
          },
          "from": {
            "type": "string"
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "text": {
            "type": "string",
            "description": "The reply without quoted history. Customer content: show as text."
          },
          "category": {
            "type": "string",
            "enum": [
              "wants_to_return",
              "question",
              "not_interested",
              "unsubscribe",
              "out_of_office",
              "other"
            ]
          },
          "summary": {
            "type": "string",
            "nullable": true,
            "description": "One line by the AI; null when keyword rules sorted it."
          },
          "needs_human": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "handled",
              "ignored"
            ]
          },
          "handled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "answer_message": {
            "type": "string",
            "nullable": true,
            "description": "The answer sent from To do (msg_…)."
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          },
          "livemode": {
            "type": "boolean"
          }
        },
        "nullable": false
      },
      "MessageReplyPage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor",
          "open_count"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageReply"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          },
          "open_count": {
            "type": "integer",
            "description": "Replies still open in this mode (To do badge)."
          }
        },
        "nullable": false
      },
      "FlowStats": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "flow",
          "kind",
          "livemode",
          "period",
          "kpis",
          "funnel",
          "holdout"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow_stats"
            ]
          },
          "flow": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "prevention",
              "recovery",
              "cancel",
              "winback"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "period": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "key",
              "from",
              "to"
            ],
            "properties": {
              "key": {
                "type": "string",
                "enum": [
                  "month",
                  "30d",
                  "90d",
                  "12m"
                ]
              },
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              }
            },
            "nullable": false
          },
          "kpis": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "key",
                "label",
                "value",
                "unit",
                "sample_size"
              ],
              "additionalProperties": false,
              "properties": {
                "key": {
                  "type": "string",
                  "example": "recovery_rate"
                },
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "number",
                  "nullable": true
                },
                "unit": {
                  "type": "string",
                  "enum": [
                    "count",
                    "money",
                    "rate",
                    "days"
                  ]
                },
                "sample_size": {
                  "type": "integer"
                },
                "pending": {
                  "type": "integer",
                  "description": "Outcomes reached but not confirmed yet (30 days)."
                },
                "currency": {
                  "type": "string"
                },
                "open_rate": {
                  "type": "number",
                  "nullable": true
                },
                "breakdown": {
                  "type": "array",
                  "description": "Per original currency: the amount and its value in the reporting currency (ECB rate of its day).",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "currency",
                      "amount_cents",
                      "converted_cents",
                      "unconverted_cents"
                    ],
                    "properties": {
                      "currency": {
                        "type": "string",
                        "example": "usd"
                      },
                      "amount_cents": {
                        "type": "integer",
                        "description": "In this currency."
                      },
                      "converted_cents": {
                        "type": "integer",
                        "description": "Its converted part, in the reporting currency."
                      },
                      "unconverted_cents": {
                        "type": "integer",
                        "description": "In this currency: amounts of days without an ECB rate, left out of the total."
                      }
                    },
                    "nullable": false
                  }
                }
              }
            },
            "description": "4 KPIs; saved / recovered / won back / prevented count confirmed outcomes."
          },
          "funnel": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "label",
                "count",
                "share"
              ],
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                },
                "share": {
                  "type": "number",
                  "nullable": true
                }
              },
              "nullable": false
            },
            "description": "5 steps: runs started in the period that reached each step, share of the first."
          },
          "holdout": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "percent",
              "runs",
              "natural_rate",
              "treated_rate",
              "lift",
              "sample_size"
            ],
            "properties": {
              "percent": {
                "type": "integer",
                "nullable": true
              },
              "runs": {
                "type": "integer"
              },
              "natural_rate": {
                "type": "number",
                "nullable": true,
                "description": "What happened without NoChurn (holdout group)."
              },
              "treated_rate": {
                "type": "number",
                "nullable": true
              },
              "lift": {
                "type": "number",
                "nullable": true
              },
              "sample_size": {
                "type": "integer"
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "Segment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "flow",
          "name",
          "position",
          "is_default",
          "conditions",
          "overrides",
          "customers_count",
          "mrr_cents",
          "counted_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "segment"
            ]
          },
          "id": {
            "type": "string",
            "example": "seg_2Kd9sL2mQx7VbN4p"
          },
          "flow": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "position": {
            "type": "integer"
          },
          "is_default": {
            "type": "boolean",
            "description": "Everyone else: always last, no conditions."
          },
          "conditions": {
            "type": "object",
            "description": "{ any: [ { all: [ { source, field, op, value } ] } ] }"
          },
          "overrides": {
            "type": "object",
            "description": "THEN: patch of the flow config, frozen on each run at entry. A send_window option left out follows the flow."
          },
          "customers_count": {
            "type": "integer",
            "nullable": true
          },
          "mrr_cents": {
            "type": "integer",
            "nullable": true
          },
          "counted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "SegmentField": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "source",
          "field",
          "type",
          "label",
          "ops"
        ],
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "stripe",
              "nochurn",
              "custom"
            ]
          },
          "field": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean",
              "date"
            ]
          },
          "label": {
            "type": "string"
          },
          "ops": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "nullable": false
      },
      "SegmentPreview": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customers_count",
          "mrr_cents",
          "counted_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "segment_preview"
            ]
          },
          "customers_count": {
            "type": "integer"
          },
          "mrr_cents": {
            "type": "integer"
          },
          "counted_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "Experiment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "flow",
          "segment",
          "name",
          "target",
          "goal_metric",
          "status",
          "auto_pick",
          "threshold",
          "min_sample",
          "started_at",
          "stopped_at",
          "winner",
          "stats_at",
          "created_at",
          "variants"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "experiment"
            ]
          },
          "id": {
            "type": "string",
            "example": "abt_5Hq2LmX9pK3sNd4v"
          },
          "flow": {
            "type": "string"
          },
          "segment": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "target": {
            "type": "string",
            "enum": [
              "offer",
              "text",
              "timing",
              "design",
              "channel"
            ]
          },
          "goal_metric": {
            "type": "string",
            "enum": [
              "saved",
              "card_updated_before_renewal",
              "recovered_within_7d",
              "won_back"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "running",
              "stopped",
              "decided"
            ]
          },
          "auto_pick": {
            "type": "boolean"
          },
          "threshold": {
            "type": "number"
          },
          "min_sample": {
            "type": "integer"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "stopped_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "winner": {
            "type": "string",
            "nullable": true
          },
          "stats_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "variants": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "label",
                "is_control",
                "weight",
                "overrides",
                "exposures",
                "conversions",
                "conversion_rate",
                "probability_best"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string",
                  "enum": [
                    "A",
                    "B",
                    "C",
                    "D"
                  ]
                },
                "is_control": {
                  "type": "boolean"
                },
                "weight": {
                  "type": "integer"
                },
                "overrides": {
                  "type": "object",
                  "description": "Patch of the flow config (on top of the segment's). A send_window option left out follows the flow."
                },
                "exposures": {
                  "type": "integer"
                },
                "conversions": {
                  "type": "integer"
                },
                "conversion_rate": {
                  "type": "number",
                  "nullable": true
                },
                "probability_best": {
                  "type": "number",
                  "nullable": true
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "UsageEventBatch": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "accepted",
          "inserted",
          "duplicates",
          "errors"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "usage_event_batch"
            ]
          },
          "accepted": {
            "type": "integer"
          },
          "inserted": {
            "type": "integer"
          },
          "duplicates": {
            "type": "integer",
            "description": "Already received (same idempotency_key)."
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "index",
                "code",
                "message"
              ],
              "properties": {
                "index": {
                  "type": "integer"
                },
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "WidgetEventBatch": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "accepted",
          "duplicates",
          "rejected"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "widget_event_batch"
            ]
          },
          "accepted": {
            "type": "integer",
            "description": "Stored or already received."
          },
          "duplicates": {
            "type": "integer",
            "description": "Already received (same idempotency_key in this mode)."
          },
          "rejected": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "index",
                "code",
                "message"
              ],
              "properties": {
                "index": {
                  "type": "integer"
                },
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "Insights": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "run",
          "insight"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "insights"
            ]
          },
          "run": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "computed_at",
              "population",
              "churned",
              "baseline_rate",
              "window_days",
              "drivers"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "computed_at": {
                "type": "string",
                "format": "date-time"
              },
              "population": {
                "type": "integer"
              },
              "churned": {
                "type": "integer"
              },
              "baseline_rate": {
                "type": "number",
                "nullable": true
              },
              "window_days": {
                "type": "integer"
              },
              "drivers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "attribute",
                    "source",
                    "bucket",
                    "customers",
                    "churned",
                    "churn_rate",
                    "relative_risk",
                    "ci_low",
                    "ci_high",
                    "q_value",
                    "significant",
                    "sample_ok",
                    "delta_vs_previous",
                    "mrr_cents"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "drv_…"
                    },
                    "attribute": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "nochurn",
                        "custom"
                      ]
                    },
                    "bucket": {
                      "type": "string"
                    },
                    "customers": {
                      "type": "integer"
                    },
                    "churned": {
                      "type": "integer"
                    },
                    "churn_rate": {
                      "type": "number"
                    },
                    "relative_risk": {
                      "type": "number",
                      "nullable": true
                    },
                    "ci_low": {
                      "type": "number",
                      "nullable": true
                    },
                    "ci_high": {
                      "type": "number",
                      "nullable": true
                    },
                    "q_value": {
                      "type": "number",
                      "nullable": true
                    },
                    "significant": {
                      "type": "boolean"
                    },
                    "sample_ok": {
                      "type": "boolean"
                    },
                    "delta_vs_previous": {
                      "type": "number",
                      "nullable": true
                    },
                    "mrr_cents": {
                      "type": "integer",
                      "description": "MRR of the active customers of the bucket."
                    }
                  },
                  "nullable": false
                }
              }
            },
            "nullable": true
          },
          "insight": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "run",
              "created_at",
              "model",
              "prompt_version",
              "headline",
              "insights",
              "data_gaps",
              "drivers"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "ins_…"
              },
              "run": {
                "type": "string"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "model": {
                "type": "string",
                "nullable": true
              },
              "prompt_version": {
                "type": "string"
              },
              "headline": {
                "type": "string",
                "nullable": true
              },
              "insights": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "data_gaps": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "drivers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "attribute",
                    "bucket",
                    "customers",
                    "churn_rate",
                    "relative_risk",
                    "significant"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "attribute": {
                      "type": "string"
                    },
                    "bucket": {
                      "type": "string"
                    },
                    "customers": {
                      "type": "integer"
                    },
                    "churn_rate": {
                      "type": "number"
                    },
                    "relative_risk": {
                      "type": "number",
                      "nullable": true
                    },
                    "significant": {
                      "type": "boolean"
                    }
                  },
                  "nullable": false
                },
                "description": "The drivers the insight cites, from its own run (may be older than `run`)."
              }
            },
            "nullable": true
          }
        },
        "nullable": false
      },
      "InsightsRefresh": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "queued"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "insights_refresh"
            ]
          },
          "queued": {
            "type": "boolean"
          }
        },
        "nullable": false
      },
      "CampaignStats": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "group_by",
          "period",
          "customers",
          "churned",
          "churn_rate",
          "tracked",
          "best",
          "worst",
          "min_sample",
          "rows"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "campaign_stats"
            ]
          },
          "group_by": {
            "type": "string"
          },
          "period": {
            "type": "string"
          },
          "customers": {
            "type": "integer"
          },
          "churned": {
            "type": "integer"
          },
          "churn_rate": {
            "type": "number",
            "nullable": true
          },
          "tracked": {
            "type": "integer"
          },
          "best": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "value",
              "relative_risk"
            ],
            "properties": {
              "value": {
                "type": "string"
              },
              "relative_risk": {
                "type": "number"
              }
            },
            "nullable": true
          },
          "worst": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "value",
              "relative_risk"
            ],
            "properties": {
              "value": {
                "type": "string"
              },
              "relative_risk": {
                "type": "number"
              }
            },
            "nullable": true
          },
          "min_sample": {
            "type": "integer"
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "value",
                "customers",
                "churned",
                "churn_rate",
                "relative_risk",
                "avg_ltv_cents",
                "avg_score",
                "mrr_lost_cents",
                "sample_ok"
              ],
              "properties": {
                "value": {
                  "type": "string"
                },
                "customers": {
                  "type": "integer"
                },
                "churned": {
                  "type": "integer"
                },
                "churn_rate": {
                  "type": "number"
                },
                "relative_risk": {
                  "type": "number",
                  "nullable": true
                },
                "avg_ltv_cents": {
                  "type": "integer"
                },
                "avg_score": {
                  "type": "number",
                  "nullable": true
                },
                "mrr_lost_cents": {
                  "type": "integer"
                },
                "sample_ok": {
                  "type": "boolean"
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "CancelSessionReport": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "step",
          "outcome",
          "locale",
          "holdout",
          "published",
          "customer",
          "subscription",
          "subscriptions",
          "reasons",
          "offer",
          "talk_url",
          "texts",
          "feedback_enabled",
          "feedback",
          "theme",
          "cancel_url_now",
          "cancel_status",
          "cancel_effective_at",
          "result",
          "access_until",
          "confirmation_email_sent",
          "review",
          "expires_at",
          "created_at",
          "reason",
          "comment"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cancel_session"
            ]
          },
          "id": {
            "type": "string",
            "example": "ses_3Ls9dK2mQx7VbN4p"
          },
          "livemode": {
            "type": "boolean"
          },
          "step": {
            "type": "string",
            "enum": [
              "choose_subscription",
              "reason",
              "offer",
              "feedback",
              "confirm",
              "done"
            ]
          },
          "outcome": {
            "type": "string",
            "nullable": true,
            "enum": [
              "saved",
              "cancelled",
              "abandoned",
              null
            ]
          },
          "locale": {
            "type": "string"
          },
          "holdout": {
            "type": "boolean"
          },
          "published": {
            "type": "boolean"
          },
          "customer": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          },
          "subscription": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "plan",
              "status",
              "amount_cents",
              "currency",
              "interval",
              "interval_count",
              "current_period_end",
              "cancel_at_period_end"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "nsub_7Hd2kQ9xLm3VbN4p"
              },
              "plan": {
                "type": "string",
                "nullable": true
              },
              "status": {
                "type": "string",
                "enum": [
                  "incomplete",
                  "incomplete_expired",
                  "trialing",
                  "active",
                  "past_due",
                  "canceled",
                  "unpaid",
                  "paused"
                ]
              },
              "amount_cents": {
                "type": "integer",
                "description": "Price per interval (quantity included), minor unit."
              },
              "currency": {
                "type": "string",
                "nullable": true
              },
              "interval": {
                "type": "string",
                "nullable": true,
                "example": "month"
              },
              "interval_count": {
                "type": "integer"
              },
              "current_period_end": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Next renewal."
              },
              "cancel_at_period_end": {
                "type": "boolean"
              }
            },
            "nullable": true,
            "description": "The subscription this session targets (offers, saved MRR, cancellation). Null at choose_subscription, for an unknown customer, or without a live subscription."
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "plan",
                "status",
                "amount_cents",
                "currency",
                "interval",
                "interval_count",
                "current_period_end",
                "cancel_at_period_end"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "nsub_7Hd2kQ9xLm3VbN4p"
                },
                "plan": {
                  "type": "string",
                  "nullable": true
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "incomplete",
                    "incomplete_expired",
                    "trialing",
                    "active",
                    "past_due",
                    "canceled",
                    "unpaid",
                    "paused"
                  ]
                },
                "amount_cents": {
                  "type": "integer",
                  "description": "Price per interval (quantity included), minor unit."
                },
                "currency": {
                  "type": "string",
                  "nullable": true
                },
                "interval": {
                  "type": "string",
                  "nullable": true,
                  "example": "month"
                },
                "interval_count": {
                  "type": "integer"
                },
                "current_period_end": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true,
                  "description": "Next renewal."
                },
                "cancel_at_period_end": {
                  "type": "boolean"
                }
              },
              "nullable": false
            },
            "description": "At choose_subscription only (several live subscriptions, none named by the token's `subscription` claim): the customer picks one (answer { subscription }) or cancels one (cancel { subscription }). Empty otherwise."
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "label",
                "free_text"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "too_expensive"
                },
                "label": {
                  "type": "string"
                },
                "free_text": {
                  "type": "boolean"
                }
              },
              "nullable": false
            }
          },
          "offer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "kind",
              "title",
              "url",
              "percent",
              "months",
              "plan_name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "discount",
                  "pause",
                  "downgrade",
                  "talk"
                ]
              },
              "title": {
                "type": "string",
                "description": "In the session's language, with the account's text for that offer kind (texts.<lang>.offer_<kind>) when set."
              },
              "url": {
                "type": "string",
                "nullable": true,
                "description": "talk: the booking link."
              },
              "percent": {
                "type": "integer",
                "nullable": true,
                "description": "discount: percent off."
              },
              "months": {
                "type": "integer",
                "nullable": true,
                "description": "discount: how many months; pause: how long."
              },
              "plan_name": {
                "type": "string",
                "nullable": true,
                "description": "downgrade: the target plan's name, when the offer has one."
              }
            },
            "nullable": true
          },
          "talk_url": {
            "type": "string",
            "nullable": true
          },
          "texts": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "feedback_enabled": {
            "type": "boolean",
            "description": "Same as feedback.enabled (kept for older widgets)."
          },
          "feedback": {
            "$ref": "#/components/schemas/CancelFeedback"
          },
          "theme": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CancelFlowTheme"
              }
            ],
            "description": "This session's design: the flow's, or its segment's / A/B variant's."
          },
          "cancel_url_now": {
            "type": "string",
            "nullable": true,
            "description": "Shown once, when the session is created: cancels at any step."
          },
          "cancel_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "done",
              "requested",
              "failed",
              null
            ]
          },
          "cancel_effective_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "result": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CancelSessionResult"
              }
            ],
            "nullable": true,
            "description": "Once an offer is accepted: outcome saved (discount, pause, downgrade), or a talk offer accepted (the session goes on). Null otherwise."
          },
          "access_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Cancelled: access lasts until then (cancel_effective_at: the period end, or now when immediate). Null otherwise, and while the cancellation is pending."
          },
          "confirmation_email_sent": {
            "type": "boolean",
            "description": "A cancellation confirmation email was actually queued. NoChurn sends none today: always false (the widget hides that fact)."
          },
          "review": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "platform",
              "url",
              "text",
              "cta"
            ],
            "properties": {
              "platform": {
                "type": "string",
                "enum": [
                  "g2",
                  "capterra",
                  "trustpilot",
                  "google",
                  "app_store",
                  "product_hunt"
                ]
              },
              "url": {
                "type": "string"
              },
              "text": {
                "type": "string"
              },
              "cta": {
                "type": "string"
              }
            },
            "nullable": true,
            "description": "Asked on the last step to happy leavers only, once per customer, never tied to an offer."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "comment": {
            "type": "string",
            "nullable": true,
            "description": "Customer text: show as text."
          }
        },
        "nullable": false
      },
      "CancelSessionReplay": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "steps",
          "reason",
          "comment",
          "feedback",
          "outcome",
          "cancel_status"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cancel_session_replay"
            ]
          },
          "id": {
            "type": "string"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Timestamped steps."
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "comment": {
            "type": "string",
            "nullable": true
          },
          "feedback": {
            "type": "string",
            "nullable": true
          },
          "outcome": {
            "type": "string",
            "nullable": true,
            "enum": [
              "saved",
              "cancelled",
              "abandoned",
              null
            ]
          },
          "cancel_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "done",
              "requested",
              "failed",
              null
            ]
          }
        },
        "nullable": false
      },
      "Integration": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "kind",
          "status",
          "external_account",
          "config",
          "last_synced_at",
          "error",
          "conversations_count",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "integration"
            ]
          },
          "id": {
            "type": "string",
            "example": "itg_…"
          },
          "kind": {
            "type": "string",
            "enum": [
              "intercom",
              "crisp",
              "zendesk",
              "hubspot",
              "helpscout",
              "gorgias",
              "slack"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "error",
              "revoked"
            ]
          },
          "external_account": {
            "type": "string",
            "nullable": true
          },
          "config": {
            "type": "object",
            "properties": {
              "channel": {
                "type": "string"
              }
            },
            "description": "Slack: the channel NoChurn posts to."
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Actionable: why it needs a reconnection."
          },
          "conversations_count": {
            "type": "integer",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "IntegrationAuthorizeUrl": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "kind",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "authorize_url"
            ]
          },
          "kind": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "nullable": false
      },
      "Feedback": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "answers_90d",
          "run",
          "items"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "feedback"
            ]
          },
          "answers_90d": {
            "type": "integer"
          },
          "run": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "computed_at",
              "summary",
              "answers_count",
              "currency",
              "themes"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "computed_at": {
                "type": "string",
                "format": "date-time"
              },
              "summary": {
                "type": "string",
                "nullable": true
              },
              "answers_count": {
                "type": "integer"
              },
              "currency": {
                "type": "string",
                "nullable": true,
                "description": "Currency of the themes' mrr_lost_cents (the reporting currency when the run was computed)."
              },
              "themes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "title",
                    "mentions",
                    "delta_pct",
                    "main_reason_key",
                    "mrr_lost_cents",
                    "unconverted_currencies",
                    "quotes"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "mentions": {
                      "type": "integer",
                      "description": "Counted in code."
                    },
                    "delta_pct": {
                      "type": "number",
                      "nullable": true
                    },
                    "main_reason_key": {
                      "type": "string",
                      "nullable": true
                    },
                    "mrr_lost_cents": {
                      "type": "integer"
                    },
                    "unconverted_currencies": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Currencies of lost MRR without an ECB rate: left out of mrr_lost_cents."
                    },
                    "quotes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Feedback item ids to quote."
                    }
                  },
                  "nullable": false
                }
              }
            },
            "nullable": true
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "source",
                "text",
                "reason_key",
                "theme",
                "plan",
                "country",
                "customer",
                "occurred_at"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "cancel_session",
                    "winback_reply",
                    "support"
                  ]
                },
                "text": {
                  "type": "string",
                  "description": "Customer text: show as text."
                },
                "reason_key": {
                  "type": "string",
                  "nullable": true
                },
                "theme": {
                  "type": "string",
                  "nullable": true
                },
                "plan": {
                  "type": "string",
                  "nullable": true
                },
                "country": {
                  "type": "string",
                  "nullable": true
                },
                "customer": {
                  "type": "string",
                  "nullable": true
                },
                "occurred_at": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "FeedbackRefresh": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "queued"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "feedback_refresh"
            ]
          },
          "queued": {
            "type": "boolean"
          }
        },
        "nullable": false
      },
      "CustomerInsight": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "summary",
          "signals",
          "next_step",
          "sources",
          "computed_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "customer_insight"
            ]
          },
          "id": {
            "type": "string",
            "example": "cai_…"
          },
          "summary": {
            "type": "string"
          },
          "signals": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "next_step": {
            "type": "string",
            "nullable": true
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "stripe",
                "custom_data",
                "support"
              ]
            }
          },
          "computed_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": true
      },
      "QuerySource": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "source",
          "label",
          "description",
          "fields"
        ],
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "customers",
              "subscriptions",
              "invoices",
              "payments",
              "cancel_sessions",
              "flow_runs",
              "usage_events",
              "messages"
            ]
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "One line: what a row of this source is."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "field",
                "type",
                "label",
                "description"
              ],
              "properties": {
                "field": {
                  "type": "string",
                  "example": "plan"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "date"
                  ]
                },
                "label": {
                  "type": "string"
                },
                "description": {
                  "type": "string",
                  "description": "One line, shown as a hint."
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "value",
                      "label"
                    ],
                    "properties": {
                      "value": {
                        "type": "string",
                        "example": "churned"
                      },
                      "label": {
                        "type": "string",
                        "example": "Churned"
                      }
                    },
                    "nullable": false
                  },
                  "maxItems": 50,
                  "description": "Enum-like fields only. closed: every allowed value. observed: the values seen for this account and mode, most frequent first (50 at most, cached 10 minutes): other values are still accepted."
                },
                "values_kind": {
                  "type": "string",
                  "enum": [
                    "closed",
                    "observed"
                  ],
                  "description": "Present with values."
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "QuerySpec": {
        "type": "object",
        "required": [
          "source"
        ],
        "description": "NoChurn query builder (no SQL). Either metrics (+ dimensions) or fields.",
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "customers",
              "subscriptions",
              "invoices",
              "payments",
              "cancel_sessions",
              "flow_runs",
              "usage_events",
              "messages"
            ]
          },
          "metrics": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "object",
              "required": [
                "fn"
              ],
              "properties": {
                "fn": {
                  "type": "string",
                  "enum": [
                    "count",
                    "count_distinct",
                    "sum",
                    "avg",
                    "min",
                    "max"
                  ]
                },
                "field": {
                  "type": "string"
                }
              }
            }
          },
          "dimensions": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "type": "object",
              "required": [
                "field"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "bucket": {
                  "type": "string",
                  "enum": [
                    "day",
                    "week",
                    "month",
                    "quarter",
                    "year"
                  ]
                }
              }
            }
          },
          "fields": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            }
          },
          "filters": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": [
                "field",
                "op"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "op": {
                  "type": "string"
                },
                "value": {}
              }
            }
          },
          "period": {
            "type": "object",
            "nullable": true,
            "properties": {
              "field": {
                "type": "string"
              },
              "last_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 730
              }
            }
          },
          "sort": {
            "type": "object",
            "nullable": true,
            "properties": {
              "by": {
                "type": "string",
                "description": "metric_0…, a dimension or a field."
              },
              "dir": {
                "type": "string",
                "enum": [
                  "asc",
                  "desc"
                ]
              }
            }
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000
          }
        }
      },
      "QueryRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "status",
          "spec",
          "columns",
          "rows",
          "row_count",
          "duration_ms",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "query_run"
            ]
          },
          "id": {
            "type": "string",
            "example": "qrn_…"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed",
              "timeout"
            ]
          },
          "spec": {
            "$ref": "#/components/schemas/QuerySpec"
          },
          "columns": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {}
            }
          },
          "row_count": {
            "type": "integer"
          },
          "duration_ms": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "QueryTranslation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "question",
          "spec",
          "explanation"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "query_translation"
            ]
          },
          "question": {
            "type": "string"
          },
          "spec": {
            "$ref": "#/components/schemas/QuerySpec"
          },
          "explanation": {
            "type": "string"
          }
        },
        "nullable": false
      },
      "SavedQuery": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "name",
          "question",
          "spec",
          "view",
          "chart",
          "created_at",
          "schedule"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "saved_query"
            ]
          },
          "id": {
            "type": "string",
            "example": "qry_…"
          },
          "name": {
            "type": "string"
          },
          "question": {
            "type": "string",
            "nullable": true
          },
          "spec": {
            "$ref": "#/components/schemas/QuerySpec"
          },
          "view": {
            "type": "string",
            "enum": [
              "table",
              "chart"
            ]
          },
          "chart": {
            "type": "object"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "schedule": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "cadence",
              "day",
              "time",
              "timezone",
              "destination",
              "target",
              "next_run_at",
              "last_run"
            ],
            "properties": {
              "cadence": {
                "type": "string",
                "enum": [
                  "daily",
                  "weekly",
                  "monthly"
                ]
              },
              "day": {
                "oneOf": [
                  {
                    "type": "string",
                    "enum": [
                      "monday",
                      "tuesday",
                      "wednesday",
                      "thursday",
                      "friday",
                      "saturday",
                      "sunday"
                    ]
                  },
                  {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 28
                  }
                ],
                "nullable": true,
                "description": "weekly: the weekday; monthly: the day of the month (1–28); daily: null."
              },
              "time": {
                "type": "string",
                "example": "08:00",
                "description": "Read only: the hour the report goes out (the hour it was first scheduled at), in timezone."
              },
              "timezone": {
                "type": "string",
                "example": "Europe/Paris",
                "description": "The account's time zone."
              },
              "destination": {
                "type": "string",
                "enum": [
                  "email",
                  "slack"
                ]
              },
              "target": {
                "type": "string",
                "nullable": true
              },
              "next_run_at": {
                "type": "string",
                "format": "date-time"
              },
              "last_run": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true
          }
        },
        "nullable": false
      },
      "BillingConnection": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "provider",
          "livemode",
          "status",
          "external_account_id",
          "business_name",
          "capabilities",
          "available",
          "webhook_url",
          "backfill",
          "webhook_secret_set",
          "settings",
          "internal_id",
          "last_event_at",
          "error",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "billing_connection"
            ]
          },
          "id": {
            "type": "string",
            "example": "bcn_…"
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "chargebee",
              "recurly",
              "maxio",
              "zuora",
              "paddle",
              "lemon_squeezy",
              "polar",
              "braintree",
              "mollie",
              "gocardless",
              "razorpay",
              "app_store",
              "google_play",
              "custom"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "error"
            ]
          },
          "external_account_id": {
            "type": "string",
            "nullable": true
          },
          "business_name": {
            "type": "string",
            "nullable": true
          },
          "capabilities": {
            "type": "object",
            "required": [
              "retries",
              "card_update",
              "offers",
              "cancel_via_api",
              "disputes",
              "fraud_warnings",
              "webhooks",
              "backfill"
            ],
            "additionalProperties": false,
            "properties": {
              "retries": {
                "type": "string",
                "enum": [
                  "nochurn",
                  "provider",
                  "none"
                ]
              },
              "card_update": {
                "type": "string",
                "enum": [
                  "hosted_page",
                  "provider_portal",
                  "none"
                ]
              },
              "offers": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "coupon",
                    "pause",
                    "downgrade"
                  ]
                }
              },
              "cancel_via_api": {
                "type": "boolean"
              },
              "disputes": {
                "type": "string",
                "enum": [
                  "evidence_api",
                  "provider_handles",
                  "refund_review",
                  "none"
                ]
              },
              "fraud_warnings": {
                "type": "boolean"
              },
              "webhooks": {
                "type": "boolean"
              },
              "backfill": {
                "type": "boolean"
              }
            }
          },
          "available": {
            "type": "boolean",
            "description": "The provider's adapter is live on this NoChurn version."
          },
          "webhook_url": {
            "type": "string",
            "nullable": true,
            "description": "Paste it in the provider's webhook settings (not for Stripe)."
          },
          "backfill": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "progress",
              "started_at",
              "finished_at",
              "error"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "running",
                  "done",
                  "failed"
                ]
              },
              "progress": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                },
                "description": "Real counts imported so far, by object (customer, subscription, invoice, payment_method)."
              },
              "started_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "error": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "message",
                  "fix",
                  "docs_url"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "enum": [
                      "import_access_denied",
                      "import_interrupted",
                      "import_failed"
                    ]
                  },
                  "message": {
                    "type": "string"
                  },
                  "fix": {
                    "type": "string"
                  },
                  "docs_url": {
                    "type": "string"
                  }
                },
                "nullable": true,
                "description": "Why the import stopped and what to do; null unless it failed."
              }
            },
            "nullable": false
          },
          "webhook_secret_set": {
            "type": "boolean",
            "description": "False until the provider's webhook secret is pasted (Recurly, Paddle, GoCardless, Razorpay): webhooks are refused until then. Always true for Stripe, for providers that sign webhooks with the API keys (Braintree) and for providers without a webhook secret (Mollie)."
          },
          "settings": {
            "type": "object",
            "additionalProperties": false,
            "description": "What the account declares for this connection (stores only; {} otherwise).",
            "properties": {
              "refund_review": {
                "type": "object",
                "additionalProperties": false,
                "description": "Store refund reviews: NoChurn answers only if customer_consented is true (your app collects the consent); sample_content_provided is sent unless a free trial is seen on the purchase.",
                "properties": {
                  "customer_consented": {
                    "type": "boolean"
                  },
                  "sample_content_provided": {
                    "type": "boolean"
                  }
                }
              },
              "push_audience": {
                "type": "string",
                "description": "Google Play: the audience of the Pub/Sub push OIDC token. Set, pushes without a valid Google-signed token are refused."
              },
              "push_service_account_email": {
                "type": "string",
                "description": "Google Play: the service account that signs the push OIDC token."
              }
            }
          },
          "internal_id": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "field",
              "defaults",
              "fields"
            ],
            "properties": {
              "field": {
                "type": "string",
                "nullable": true,
                "description": "The field this connection names (PATCH internal_id_field); null = defaults.",
                "example": "custom_fields.user_id"
              },
              "defaults": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "custom_fields.user_id",
                  "code"
                ],
                "description": "Read in this order when field is null; user_id is the account's external_id_metadata_key."
              },
              "fields": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "code",
                  "custom_fields.<name>"
                ],
                "description": "The shapes a field may take for this tool (<name> = one key)."
              }
            },
            "nullable": true,
            "description": "Where NoChurn reads your app's id for a customer (your user id, stored as external_id) in this tool; null for a tool that carries none (stores, universal webhook). Nothing found: the email decides."
          },
          "last_event_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "webhook_setup": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "string"
            },
            "description": "Only in the response that creates the connection: webhook credentials to paste in the provider (e.g. Chargebee Basic auth). Shown once."
          }
        },
        "nullable": false
      },
      "BillingProvider": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "provider",
          "family",
          "available",
          "connect",
          "credentials",
          "capabilities",
          "locked",
          "webhook_secret",
          "hint",
          "docs_url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "billing_provider"
            ]
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "chargebee",
              "recurly",
              "maxio",
              "zuora",
              "paddle",
              "lemon_squeezy",
              "polar",
              "braintree",
              "mollie",
              "gocardless",
              "razorpay",
              "app_store",
              "google_play",
              "custom"
            ]
          },
          "family": {
            "type": "string",
            "enum": [
              "billing",
              "merchant_of_record",
              "payments",
              "stores",
              "custom"
            ]
          },
          "available": {
            "type": "boolean",
            "description": "Its adapter ships in this version; otherwise \"Coming soon\"."
          },
          "connect": {
            "type": "string",
            "enum": [
              "oauth",
              "credentials"
            ]
          },
          "credentials": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "secret",
                "hint"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "secret": {
                  "type": "boolean"
                },
                "hint": {
                  "type": "string"
                },
                "options": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "nullable": false
            }
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": true,
            "description": "Default capabilities (see BillingConnection.capabilities)."
          },
          "locked": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Capabilities the provider decides (cannot be changed per connection)."
          },
          "webhook_secret": {
            "type": "string",
            "enum": [
              "generated",
              "pasted",
              "none"
            ],
            "description": "generated: NoChurn shows webhook credentials once at creation; pasted: create the endpoint on webhook_url, then PATCH webhook_secret."
          },
          "hint": {
            "type": "string",
            "nullable": true,
            "description": "What the provider needs beyond credentials, for the connection screen (stores: how customers get linked)."
          },
          "docs_url": {
            "type": "string",
            "example": "https://nochurn.sh/docs/integrations/lemon-squeezy/",
            "description": "The tool's integration guide: https://nochurn.sh/docs/integrations/<provider, _ written ->/."
          }
        },
        "nullable": false
      },
      "BillingMapping": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "billing_connection",
          "active",
          "activated_at",
          "rules",
          "types",
          "fields",
          "transforms",
          "sample",
          "summary"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "billing_mapping"
            ]
          },
          "billing_connection": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rules": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "{ when: { path, equals | in }, type, event_id?, occurred_at?, fields: { \"customer.id\": \"$.data.customer\", \"invoice.amount_due_cents\": { path, transform } | { value } } }"
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "transforms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sample": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "received_at",
                "payload",
                "result"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "bws_..."
                },
                "received_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "payload": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "The webhook as received (data, never code)."
                },
                "result": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "status",
                    "rule",
                    "type",
                    "event_id",
                    "data",
                    "errors"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "event",
                        "ignored",
                        "error"
                      ],
                      "description": "event: recognized; ignored: no rule matched; error: a rule matched but a field failed."
                    },
                    "rule": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Index of the rule that matched."
                    },
                    "type": {
                      "type": "string",
                      "nullable": true
                    },
                    "event_id": {
                      "type": "string",
                      "nullable": true
                    },
                    "data": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "The normalized event NoChurn would apply."
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "nullable": false
                }
              },
              "nullable": false
            }
          },
          "summary": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "total",
              "events",
              "errors",
              "ignored"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "events": {
                "type": "integer"
              },
              "errors": {
                "type": "integer"
              },
              "ignored": {
                "type": "integer"
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "Dispute": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "provider",
          "billing_connection",
          "external_id",
          "charge",
          "customer",
          "customer_name",
          "customer_email",
          "customer_plan",
          "amount_cents",
          "currency",
          "reason",
          "status",
          "evidence_due_by",
          "opened_at",
          "outcome_at",
          "submitted_at",
          "submitted_by",
          "provider_submitted",
          "read_only",
          "capability",
          "evidence_count",
          "draft_status",
          "win_score",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "dispute"
            ]
          },
          "id": {
            "type": "string",
            "example": "dsp_…"
          },
          "livemode": {
            "type": "boolean"
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "chargebee",
              "recurly",
              "maxio",
              "zuora",
              "paddle",
              "lemon_squeezy",
              "polar",
              "braintree",
              "mollie",
              "gocardless",
              "razorpay",
              "app_store",
              "google_play",
              "custom"
            ]
          },
          "billing_connection": {
            "type": "string"
          },
          "external_id": {
            "type": "string",
            "example": "dp_…"
          },
          "charge": {
            "type": "string",
            "nullable": true,
            "example": "ch_…"
          },
          "customer": {
            "type": "string",
            "nullable": true
          },
          "customer_name": {
            "type": "string",
            "nullable": true
          },
          "customer_email": {
            "type": "string",
            "nullable": true
          },
          "customer_plan": {
            "type": "string",
            "nullable": true,
            "description": "Plan of the customer's latest subscription in this mode."
          },
          "amount_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "example": "subscription_canceled"
          },
          "status": {
            "type": "string",
            "enum": [
              "needs_response",
              "under_review",
              "won",
              "lost",
              "accepted",
              "warning_needs_response",
              "warning_under_review",
              "warning_closed",
              "prevented",
              "no_decision"
            ]
          },
          "evidence_due_by": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "opened_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "outcome_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "submitted_by": {
            "type": "string",
            "nullable": true,
            "description": "usr_… or system (auto-submit)."
          },
          "provider_submitted": {
            "type": "boolean",
            "description": "The provider already has a reply (Stripe Smart Disputes, its dashboard): NoChurn will not send."
          },
          "read_only": {
            "type": "boolean",
            "description": "Nothing can be sent or edited: closed, submitted, or handled by the provider."
          },
          "capability": {
            "type": "string",
            "enum": [
              "evidence_api",
              "provider_handles",
              "refund_review",
              "none"
            ]
          },
          "evidence_count": {
            "type": "integer"
          },
          "draft_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "draft",
              "approved",
              "sent",
              null
            ]
          },
          "win_score": {
            "type": "object",
            "nullable": true,
            "required": [
              "score",
              "source",
              "confidence",
              "factors",
              "evidence_count",
              "model",
              "set_by",
              "computed_at"
            ],
            "description": "Chance of winning, 0–100: the AI's estimate from the evidence (an estimate, not a measured probability), or a person's (source manual). Null until estimated.",
            "properties": {
              "score": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100
              },
              "source": {
                "type": "string",
                "enum": [
                  "ai",
                  "manual"
                ]
              },
              "confidence": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "low",
                  "medium",
                  "high",
                  null
                ]
              },
              "factors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "label",
                    "effect"
                  ],
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "effect": {
                      "type": "string",
                      "enum": [
                        "up",
                        "down"
                      ]
                    }
                  }
                }
              },
              "evidence_count": {
                "type": "integer",
                "nullable": true
              },
              "model": {
                "type": "string",
                "nullable": true
              },
              "set_by": {
                "type": "string",
                "nullable": true
              },
              "computed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "DisputeDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "provider",
          "billing_connection",
          "external_id",
          "charge",
          "customer",
          "customer_name",
          "customer_email",
          "customer_plan",
          "amount_cents",
          "currency",
          "reason",
          "status",
          "evidence_due_by",
          "opened_at",
          "outcome_at",
          "submitted_at",
          "submitted_by",
          "provider_submitted",
          "read_only",
          "capability",
          "evidence_count",
          "draft_status",
          "win_score",
          "created_at",
          "evidence",
          "draft",
          "charge_details",
          "recommendation"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "dispute"
            ]
          },
          "id": {
            "type": "string",
            "example": "dsp_…"
          },
          "livemode": {
            "type": "boolean"
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "chargebee",
              "recurly",
              "maxio",
              "zuora",
              "paddle",
              "lemon_squeezy",
              "polar",
              "braintree",
              "mollie",
              "gocardless",
              "razorpay",
              "app_store",
              "google_play",
              "custom"
            ]
          },
          "billing_connection": {
            "type": "string"
          },
          "external_id": {
            "type": "string",
            "example": "dp_…"
          },
          "charge": {
            "type": "string",
            "nullable": true,
            "example": "ch_…"
          },
          "customer": {
            "type": "string",
            "nullable": true
          },
          "customer_name": {
            "type": "string",
            "nullable": true
          },
          "customer_email": {
            "type": "string",
            "nullable": true
          },
          "customer_plan": {
            "type": "string",
            "nullable": true,
            "description": "Plan of the customer's latest subscription in this mode."
          },
          "amount_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "example": "subscription_canceled"
          },
          "status": {
            "type": "string",
            "enum": [
              "needs_response",
              "under_review",
              "won",
              "lost",
              "accepted",
              "warning_needs_response",
              "warning_under_review",
              "warning_closed",
              "prevented",
              "no_decision"
            ]
          },
          "evidence_due_by": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "opened_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "outcome_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "submitted_by": {
            "type": "string",
            "nullable": true,
            "description": "usr_… or system (auto-submit)."
          },
          "provider_submitted": {
            "type": "boolean",
            "description": "The provider already has a reply (Stripe Smart Disputes, its dashboard): NoChurn will not send."
          },
          "read_only": {
            "type": "boolean",
            "description": "Nothing can be sent or edited: closed, submitted, or handled by the provider."
          },
          "capability": {
            "type": "string",
            "enum": [
              "evidence_api",
              "provider_handles",
              "refund_review",
              "none"
            ]
          },
          "evidence_count": {
            "type": "integer"
          },
          "draft_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "draft",
              "approved",
              "sent",
              null
            ]
          },
          "win_score": {
            "type": "object",
            "nullable": true,
            "required": [
              "score",
              "source",
              "confidence",
              "factors",
              "evidence_count",
              "model",
              "set_by",
              "computed_at"
            ],
            "description": "Chance of winning, 0–100: the AI's estimate from the evidence (an estimate, not a measured probability), or a person's (source manual). Null until estimated.",
            "properties": {
              "score": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100
              },
              "source": {
                "type": "string",
                "enum": [
                  "ai",
                  "manual"
                ]
              },
              "confidence": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "low",
                  "medium",
                  "high",
                  null
                ]
              },
              "factors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "label",
                    "effect"
                  ],
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "effect": {
                      "type": "string",
                      "enum": [
                        "up",
                        "down"
                      ]
                    }
                  }
                }
              },
              "evidence_count": {
                "type": "integer",
                "nullable": true
              },
              "model": {
                "type": "string",
                "nullable": true
              },
              "set_by": {
                "type": "string",
                "nullable": true
              },
              "computed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DisputeEvidenceItem"
            }
          },
          "draft": {
            "$ref": "#/components/schemas/DisputeDraft",
            "nullable": true
          },
          "charge_details": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "paid_at",
              "plan",
              "invoice",
              "period_end"
            ],
            "properties": {
              "paid_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "plan": {
                "type": "string",
                "nullable": true
              },
              "invoice": {
                "type": "string",
                "nullable": true,
                "example": "ninv_…"
              },
              "period_end": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "End of the invoiced period."
              }
            },
            "nullable": true,
            "description": "The disputed charge as NoChurn synced it; null when the payment is unknown."
          },
          "recommendation": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "suggestion",
              "reason",
              "evidence_count",
              "amount_cents",
              "contest_cost_cents",
              "currency",
              "win_score",
              "expected_gain_cents"
            ],
            "properties": {
              "suggestion": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "accept",
                  "contest",
                  null
                ],
                "description": "accept: no evidence from a record and the amount is at most the cost of contesting. null: not enough to compare."
              },
              "reason": {
                "type": "string"
              },
              "evidence_count": {
                "type": "integer",
                "description": "Included evidence backed by a record (NoChurn's own statements excluded)."
              },
              "amount_cents": {
                "type": "integer",
                "nullable": true,
                "description": "Disputed amount in `currency`; null without an exchange rate."
              },
              "contest_cost_cents": {
                "type": "integer",
                "nullable": true,
                "description": "From disputes_settings; null when not set."
              },
              "currency": {
                "type": "string"
              },
              "win_score": {
                "type": "integer",
                "nullable": true,
                "description": "Chance of winning used (AI or a person)."
              },
              "expected_gain_cents": {
                "type": "integer",
                "nullable": true,
                "description": "Chance × amount, compared with the cost of contesting; null without a score and a cost."
              }
            },
            "nullable": true,
            "description": "Whether contesting is worth it; null once nothing can be sent. A suggestion only: NoChurn never accepts by itself."
          }
        },
        "nullable": false
      },
      "DisputeEvidenceItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "kind",
          "title",
          "content",
          "source_type",
          "source_id",
          "occurred_at",
          "included",
          "position",
          "file"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "dev_…"
          },
          "object": {
            "type": "string",
            "enum": [
              "dispute_evidence_item"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "communication",
              "policy",
              "usage",
              "reminder",
              "cancel_history",
              "refund_history",
              "receipt",
              "other"
            ]
          },
          "title": {
            "type": "string"
          },
          "content": {
            "type": "string",
            "nullable": true
          },
          "source_type": {
            "type": "string",
            "nullable": true,
            "example": "cancel_session"
          },
          "source_id": {
            "type": "string",
            "nullable": true
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "included": {
            "type": "boolean"
          },
          "position": {
            "type": "integer"
          },
          "file": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "content_type",
              "byte_size",
              "sent_to_provider"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "content_type": {
                "type": "string",
                "enum": [
                  "application/pdf",
                  "image/jpeg",
                  "image/png"
                ]
              },
              "byte_size": {
                "type": "integer"
              },
              "sent_to_provider": {
                "type": "boolean",
                "description": "The provider has a copy (given when the reply was sent)."
              }
            },
            "nullable": true,
            "description": "A file attached by a person (source_type file): PDF, JPEG or PNG, 5 MB at most. null for other evidence."
          }
        },
        "nullable": false
      },
      "DisputeDraft": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "text",
          "cited_item_ids",
          "model",
          "status",
          "edited_by",
          "edited_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "ddr_…"
          },
          "object": {
            "type": "string",
            "enum": [
              "dispute_draft"
            ]
          },
          "text": {
            "type": "string"
          },
          "cited_item_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Evidence items the reply relies on (dev_…)."
          },
          "model": {
            "type": "string",
            "nullable": true,
            "description": "AI model, or \"template\" when the AI answer was rejected."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "approved",
              "sent"
            ]
          },
          "edited_by": {
            "type": "string",
            "nullable": true
          },
          "edited_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": true
      },
      "DisputesSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "days",
          "open",
          "due_within_72h",
          "next_due_at",
          "drafts_ready",
          "open_amount",
          "won",
          "lost",
          "decided",
          "win_rate",
          "win_score_accuracy",
          "won_amount",
          "fraud_warnings_open",
          "fraud_warnings_refunded"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "disputes_summary"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "days": {
            "type": "integer"
          },
          "open": {
            "type": "integer"
          },
          "due_within_72h": {
            "type": "integer"
          },
          "open_amount": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "currency",
                "amount_cents"
              ],
              "properties": {
                "currency": {
                  "type": "string"
                },
                "amount_cents": {
                  "type": "integer"
                }
              },
              "nullable": false
            }
          },
          "next_due_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Soonest upcoming deadline of an open dispute."
          },
          "drafts_ready": {
            "type": "integer",
            "description": "Open disputes NoChurn can send whose reply is drafted: they wait for a review."
          },
          "won": {
            "type": "integer"
          },
          "lost": {
            "type": "integer",
            "description": "Lost or accepted."
          },
          "decided": {
            "type": "integer",
            "description": "Sample size of win_rate."
          },
          "win_rate": {
            "type": "number",
            "nullable": true
          },
          "won_amount": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "currency",
                "amount_cents"
              ],
              "properties": {
                "currency": {
                  "type": "string"
                },
                "amount_cents": {
                  "type": "integer"
                }
              },
              "nullable": false
            },
            "description": "Saved: disputes won and closed only."
          },
          "win_score_accuracy": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "decided",
              "right",
              "rate"
            ],
            "properties": {
              "decided": {
                "type": "integer",
                "description": "Sample size."
              },
              "right": {
                "type": "integer"
              },
              "rate": {
                "type": "number",
                "nullable": true
              }
            },
            "nullable": false,
            "description": "How often the AI's chance of winning called the outcome (50+ = won), on decided disputes it scored."
          },
          "fraud_warnings_open": {
            "type": "integer"
          },
          "fraud_warnings_refunded": {
            "type": "integer"
          }
        },
        "nullable": false
      },
      "FraudWarning": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "livemode",
          "provider",
          "external_id",
          "charge",
          "customer",
          "customer_name",
          "customer_email",
          "customer_plan",
          "amount_cents",
          "currency",
          "fraud_type",
          "actionable",
          "action",
          "acted_by",
          "acted_at",
          "note",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "efw_…"
          },
          "object": {
            "type": "string",
            "enum": [
              "fraud_warning"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "provider": {
            "type": "string"
          },
          "external_id": {
            "type": "string",
            "example": "issfr_…"
          },
          "charge": {
            "type": "string",
            "nullable": true
          },
          "customer": {
            "type": "string",
            "nullable": true
          },
          "customer_name": {
            "type": "string",
            "nullable": true
          },
          "customer_email": {
            "type": "string",
            "nullable": true
          },
          "customer_plan": {
            "type": "string",
            "nullable": true
          },
          "amount_cents": {
            "type": "integer",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "fraud_type": {
            "type": "string",
            "nullable": true,
            "example": "made_with_stolen_card"
          },
          "actionable": {
            "type": "boolean"
          },
          "action": {
            "type": "string",
            "enum": [
              "none",
              "refunded",
              "dismissed"
            ]
          },
          "acted_by": {
            "type": "string",
            "nullable": true
          },
          "acted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "FlowNotifications": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "events",
          "slack",
          "intercom"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "flow_notifications"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "type",
                "flow"
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "recovery.opened",
                    "recovery.recovered",
                    "recovery.lost",
                    "cancel_session.offer_accepted",
                    "cancel_session.completed",
                    "customer.flagged",
                    "winback.reactivated"
                  ]
                },
                "flow": {
                  "type": "string",
                  "enum": [
                    "prevention",
                    "recovery",
                    "cancel",
                    "winback"
                  ]
                }
              },
              "nullable": false
            },
            "description": "The events that can be notified, with the flow that emits them."
          },
          "slack": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "kind",
              "connected",
              "integration_status",
              "workspace",
              "events",
              "channel",
              "notes_access",
              "last_delivery_at",
              "last_error"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "slack",
                  "intercom_note"
                ]
              },
              "connected": {
                "type": "boolean",
                "description": "The integration (Slack, Intercom) is connected and active."
              },
              "integration_status": {
                "type": "string",
                "enum": [
                  "active",
                  "error",
                  "revoked"
                ],
                "nullable": true,
                "description": "null: never connected."
              },
              "workspace": {
                "type": "string",
                "nullable": true,
                "description": "The Slack workspace or Intercom app."
              },
              "events": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "recovery.opened",
                    "recovery.recovered",
                    "recovery.lost",
                    "cancel_session.offer_accepted",
                    "cancel_session.completed",
                    "customer.flagged",
                    "winback.reactivated"
                  ]
                },
                "description": "Empty: nothing is sent."
              },
              "channel": {
                "type": "string",
                "nullable": true,
                "description": "Slack only: where messages go (the channel chosen for Slack alerts until one is saved here)."
              },
              "notes_access": {
                "type": "string",
                "enum": [
                  "unknown",
                  "ok",
                  "reconnect_required",
                  null
                ],
                "nullable": true,
                "description": "Intercom only: whether the install may write notes on contacts. reconnect_required: Intercom refused a note, reconnect it."
              },
              "last_delivery_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "last_error": {
                "type": "string",
                "nullable": true,
                "description": "Why the last notification failed, with what to do."
              }
            },
            "nullable": false
          },
          "intercom": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "kind",
              "connected",
              "integration_status",
              "workspace",
              "events",
              "channel",
              "notes_access",
              "last_delivery_at",
              "last_error"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "slack",
                  "intercom_note"
                ]
              },
              "connected": {
                "type": "boolean",
                "description": "The integration (Slack, Intercom) is connected and active."
              },
              "integration_status": {
                "type": "string",
                "enum": [
                  "active",
                  "error",
                  "revoked"
                ],
                "nullable": true,
                "description": "null: never connected."
              },
              "workspace": {
                "type": "string",
                "nullable": true,
                "description": "The Slack workspace or Intercom app."
              },
              "events": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "recovery.opened",
                    "recovery.recovered",
                    "recovery.lost",
                    "cancel_session.offer_accepted",
                    "cancel_session.completed",
                    "customer.flagged",
                    "winback.reactivated"
                  ]
                },
                "description": "Empty: nothing is sent."
              },
              "channel": {
                "type": "string",
                "nullable": true,
                "description": "Slack only: where messages go (the channel chosen for Slack alerts until one is saved here)."
              },
              "notes_access": {
                "type": "string",
                "enum": [
                  "unknown",
                  "ok",
                  "reconnect_required",
                  null
                ],
                "nullable": true,
                "description": "Intercom only: whether the install may write notes on contacts. reconnect_required: Intercom refused a note, reconnect it."
              },
              "last_delivery_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "last_error": {
                "type": "string",
                "nullable": true,
                "description": "Why the last notification failed, with what to do."
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "OauthGrant": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "client",
          "user",
          "scopes",
          "live_read",
          "live_write",
          "last_used_at",
          "revoked_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "oauth_grant"
            ]
          },
          "id": {
            "type": "string",
            "example": "ogr_V9xYpL4aZf3kQ8m2"
          },
          "client": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "verified",
              "verified_name",
              "client_uri"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "verified": {
                "type": "boolean",
                "description": "true when every redirect URI of the client is a known callback of one vendor (Claude, ChatGPT, Cursor; Oauth::VerifiedClients), never because of its name. Loopback redirects are never verified."
              },
              "verified_name": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "Claude",
                  "ChatGPT",
                  "Cursor",
                  null
                ],
                "description": "The vendor's canonical name when verified (show it instead of name), null otherwise."
              },
              "client_uri": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": false
          },
          "user": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "email",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "email": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": false,
            "description": "Who connected the app. The app acts with this user's role."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "configure",
                "sandbox"
              ]
            }
          },
          "live_read": {
            "type": "boolean",
            "description": "The app may read live data (NoChurn-Mode: live, reads only). Off unless a human allowed it."
          },
          "live_write": {
            "type": "string",
            "enum": [
              "approval_required"
            ],
            "description": "Changes in live mode always go through a live request."
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "LiveRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "action",
          "title",
          "summary",
          "params",
          "precondition",
          "reason",
          "status",
          "requested_by",
          "approval_url",
          "decided_by",
          "decided_at",
          "result",
          "error",
          "api_key",
          "response",
          "expires_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "live_request"
            ]
          },
          "id": {
            "type": "string",
            "example": "lrq_L4aZf3kQ8m2V9xYp"
          },
          "action": {
            "type": "string",
            "enum": [
              "create_live_api_key",
              "publish_flow_live",
              "set_billing_source",
              "create_webhook_endpoint",
              "update_webhook_endpoint",
              "delete_webhook_endpoint",
              "test_webhook_endpoint",
              "create_attribute_definition",
              "update_attribute_definition",
              "create_customer",
              "update_customer",
              "set_customer_attributes",
              "record_usage_event",
              "record_usage_events",
              "create_subscription",
              "update_subscription",
              "record_payment"
            ],
            "description": "The operation (LiveRequestCreate lists each one with its params)."
          },
          "title": {
            "type": "string",
            "description": "The operation's name, e.g. Add a live webhook endpoint."
          },
          "summary": {
            "type": "string",
            "description": "What approving does, written by NoChurn from the params (never by the AI app)."
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "The operation's params as checked by NoChurn (LiveRequestParams*), e.g. a customer reference resolved to its cst_… id."
          },
          "precondition": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "kind",
              "object",
              "id",
              "expected",
              "status",
              "description"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "draft_digest",
                  "current_value",
                  "record_version",
                  "record_exists"
                ]
              },
              "object": {
                "type": "string",
                "nullable": true,
                "description": "flow, billing_source, webhook_endpoint, attribute_definition, customer, subscription"
              },
              "id": {
                "type": "string",
                "nullable": true
              },
              "expected": {
                "type": "string",
                "nullable": true,
                "description": "current_value only: the value when the app asked."
              },
              "status": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "holds",
                  "changed",
                  null
                ],
                "description": "Pending requests only: checked now."
              },
              "description": {
                "type": "string"
              }
            },
            "nullable": true,
            "description": "What must still hold at approval, else the request fails without acting."
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "description": "Written by the AI app: untrusted text, show it as text."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected",
              "expired",
              "failed"
            ]
          },
          "requested_by": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "name",
              "grant_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "ai_agent"
                ]
              },
              "name": {
                "type": "string"
              },
              "grant_id": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": false
          },
          "approval_url": {
            "type": "string",
            "description": "Dashboard page where an owner or admin approves or rejects it."
          },
          "decided_by": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "email": {
                "type": "string"
              }
            },
            "nullable": true
          },
          "decided_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "result": {
            "type": "object",
            "additionalProperties": true,
            "description": "What the approval produced: object and id only (plus api_key_id, flow_version_id, billing_source for the first three operations). Never live data."
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "failed: why the action did not run."
          },
          "api_key": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ApiKey"
              }
            ],
            "nullable": true,
            "description": "Approval of create_live_api_key only: the new live key with its secret, shown once to the approver."
          },
          "response": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Approval answer only, to the approver: the operation's API response (it may hold a secret shown once: a live key's token, a webhook endpoint's signing secret). Never stored."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "nullable": false
      },
      "LiveRequestParamsCreateLiveApiKey": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "kind": {
            "type": "string",
            "enum": [
              "secret",
              "publishable"
            ]
          },
          "access": {
            "type": "string",
            "enum": [
              "full",
              "restricted"
            ]
          },
          "permissions": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "none",
                "read",
                "write"
              ]
            }
          }
        },
        "description": "Params of create_live_api_key: Create a live API key."
      },
      "LiveRequestCreateCreateLiveApiKey": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "create_live_api_key"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsCreateLiveApiKey"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsPublishFlowLive": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "flow_id"
        ],
        "properties": {
          "flow_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "flw_… or a kind: prevention, recovery, cancel, winback"
          }
        },
        "description": "Params of publish_flow_live: Publish a flow to live mode."
      },
      "LiveRequestCreatePublishFlowLive": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "publish_flow_live"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsPublishFlowLive"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsSetBillingSource": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "billing_source"
        ],
        "properties": {
          "billing_source": {
            "type": "string",
            "enum": [
              "stripe",
              "api"
            ]
          }
        },
        "description": "Params of set_billing_source: Change the billing source."
      },
      "LiveRequestCreateSetBillingSource": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "set_billing_source"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsSetBillingSource"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsCreateWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "minLength": 8,
            "maxLength": 2000
          },
          "events": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "*"
                ]
              },
              {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "string",
                  "enum": [
                    "recovery.opened",
                    "recovery.recovered",
                    "recovery.lost",
                    "outcome.confirmed",
                    "outcome.reverted",
                    "billing.retry_requested",
                    "billing.cancel_requested",
                    "billing.discount_requested",
                    "billing.pause_requested",
                    "billing.downgrade_requested",
                    "customer.scored",
                    "customer.flagged",
                    "customer.access_restricted",
                    "customer.access_restored",
                    "cancel_session.started",
                    "cancel_session.offer_accepted",
                    "cancel_session.completed",
                    "winback.reactivated",
                    "*"
                  ]
                }
              }
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 255
          }
        },
        "description": "Params of create_webhook_endpoint: Add a live webhook endpoint."
      },
      "LiveRequestCreateCreateWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "create_webhook_endpoint"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsCreateWebhookEndpoint"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsUpdateWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "url": {
            "type": "string",
            "minLength": 8,
            "maxLength": 2000
          },
          "events": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "*"
                ]
              },
              {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "string",
                  "enum": [
                    "recovery.opened",
                    "recovery.recovered",
                    "recovery.lost",
                    "outcome.confirmed",
                    "outcome.reverted",
                    "billing.retry_requested",
                    "billing.cancel_requested",
                    "billing.discount_requested",
                    "billing.pause_requested",
                    "billing.downgrade_requested",
                    "customer.scored",
                    "customer.flagged",
                    "customer.access_restricted",
                    "customer.access_restored",
                    "cancel_session.started",
                    "cancel_session.offer_accepted",
                    "cancel_session.completed",
                    "winback.reactivated",
                    "*"
                  ]
                }
              }
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 255
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ]
          }
        },
        "description": "Params of update_webhook_endpoint: Change a live webhook endpoint."
      },
      "LiveRequestCreateUpdateWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "update_webhook_endpoint"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsUpdateWebhookEndpoint"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsDeleteWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        },
        "description": "Params of delete_webhook_endpoint: Remove a live webhook endpoint."
      },
      "LiveRequestCreateDeleteWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "delete_webhook_endpoint"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsDeleteWebhookEndpoint"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsTestWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "enum": [
              "recovery.opened",
              "recovery.recovered",
              "recovery.lost",
              "outcome.confirmed",
              "outcome.reverted",
              "billing.retry_requested",
              "billing.cancel_requested",
              "billing.discount_requested",
              "billing.pause_requested",
              "billing.downgrade_requested",
              "customer.scored",
              "customer.flagged",
              "customer.access_restricted",
              "customer.access_restored",
              "cancel_session.started",
              "cancel_session.offer_accepted",
              "cancel_session.completed",
              "winback.reactivated"
            ]
          }
        },
        "description": "Params of test_webhook_endpoint: Send a test webhook to a live endpoint."
      },
      "LiveRequestCreateTestWebhookEndpoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "test_webhook_endpoint"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsTestWebhookEndpoint"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsCreateAttributeDefinition": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "key",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean",
              "date"
            ]
          },
          "sensitive": {
            "type": "boolean"
          }
        },
        "description": "Params of create_attribute_definition: Declare a live customer attribute."
      },
      "LiveRequestCreateCreateAttributeDefinition": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "create_attribute_definition"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsCreateAttributeDefinition"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsUpdateAttributeDefinition": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "key",
          "sensitive"
        ],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "sensitive": {
            "type": "boolean"
          }
        },
        "description": "Params of update_attribute_definition: Change a live customer attribute."
      },
      "LiveRequestCreateUpdateAttributeDefinition": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "update_attribute_definition"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsUpdateAttributeDefinition"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsCreateCustomer": {
        "type": "object",
        "additionalProperties": false,
        "required": [],
        "properties": {
          "external_id": {
            "type": "string",
            "maxLength": 255
          },
          "email": {
            "type": "string",
            "maxLength": 255
          },
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "locale": {
            "type": "string",
            "maxLength": 255
          },
          "country": {
            "type": "string",
            "maxLength": 255
          },
          "region": {
            "type": "string",
            "maxLength": 255
          },
          "timezone": {
            "type": "string",
            "maxLength": 255
          }
        },
        "description": "Params of create_customer: Create a live customer."
      },
      "LiveRequestCreateCreateCustomer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "create_customer"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsCreateCustomer"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsUpdateCustomer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "attributes": {
            "type": "object",
            "description": "Custom attributes; null removes a key.",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                }
              ],
              "nullable": true
            }
          },
          "external_id": {
            "type": "string",
            "maxLength": 255
          },
          "email": {
            "type": "string",
            "maxLength": 255
          },
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "locale": {
            "type": "string",
            "maxLength": 255
          },
          "country": {
            "type": "string",
            "maxLength": 255
          },
          "region": {
            "type": "string",
            "maxLength": 255
          },
          "timezone": {
            "type": "string",
            "maxLength": 255
          }
        },
        "description": "Params of update_customer: Change a live customer."
      },
      "LiveRequestCreateUpdateCustomer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "update_customer"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsUpdateCustomer"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsSetCustomerAttributes": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "attributes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "attributes": {
            "type": "object",
            "description": "Custom attributes; null removes a key.",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                }
              ],
              "nullable": true
            }
          }
        },
        "description": "Params of set_customer_attributes: Set attributes on a live customer."
      },
      "LiveRequestCreateSetCustomerAttributes": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "set_customer_attributes"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsSetCustomerAttributes"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsRecordUsageEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "customer",
          "name"
        ],
        "properties": {
          "customer": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "timestamp": {
            "type": "string",
            "maxLength": 40,
            "description": "ISO 8601"
          },
          "properties": {
            "type": "object"
          },
          "idempotency_key": {
            "type": "string",
            "maxLength": 255
          }
        },
        "description": "Params of record_usage_event: Record a live usage event."
      },
      "LiveRequestCreateRecordUsageEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "record_usage_event"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsRecordUsageEvent"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsRecordUsageEvents": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "events"
        ],
        "properties": {
          "events": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "customer",
                "name"
              ],
              "properties": {
                "customer": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 255
                },
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100
                },
                "timestamp": {
                  "type": "string",
                  "maxLength": 40,
                  "description": "ISO 8601"
                },
                "properties": {
                  "type": "object"
                },
                "idempotency_key": {
                  "type": "string",
                  "maxLength": 255
                }
              }
            }
          }
        },
        "description": "Params of record_usage_events: Record live usage events."
      },
      "LiveRequestCreateRecordUsageEvents": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "record_usage_events"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsRecordUsageEvents"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsCreateSubscription": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "external_id",
          "customer"
        ],
        "properties": {
          "external_id": {
            "type": "string",
            "maxLength": 255
          },
          "customer": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 255
              },
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "external_id": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "email": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  }
                }
              }
            ],
            "description": "cst_…, cus_… or your own id, or { external_id, email, name }"
          },
          "plan": {
            "type": "string",
            "maxLength": 255
          },
          "amount": {
            "type": "integer",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "interval": {
            "type": "string",
            "enum": [
              "day",
              "week",
              "month",
              "year"
            ]
          },
          "interval_count": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "maxLength": 255
          },
          "current_period_end": {
            "type": "string",
            "maxLength": 40,
            "description": "ISO 8601"
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "started_at": {
            "type": "string",
            "maxLength": 40,
            "description": "ISO 8601"
          }
        },
        "description": "Params of create_subscription: Record a live subscription."
      },
      "LiveRequestCreateCreateSubscription": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "create_subscription"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsCreateSubscription"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsUpdateSubscription": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "plan": {
            "type": "string",
            "maxLength": 255
          },
          "amount": {
            "type": "integer",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "interval": {
            "type": "string",
            "enum": [
              "day",
              "week",
              "month",
              "year"
            ]
          },
          "interval_count": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "maxLength": 255
          },
          "current_period_end": {
            "type": "string",
            "maxLength": 40,
            "description": "ISO 8601"
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "started_at": {
            "type": "string",
            "maxLength": 40,
            "description": "ISO 8601"
          }
        },
        "description": "Params of update_subscription: Change a live subscription."
      },
      "LiveRequestCreateUpdateSubscription": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "update_subscription"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsUpdateSubscription"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestParamsRecordPayment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "external_id",
          "customer",
          "amount",
          "currency",
          "status"
        ],
        "properties": {
          "external_id": {
            "type": "string",
            "maxLength": 255
          },
          "customer": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 255
              },
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "external_id": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "email": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  }
                }
              }
            ],
            "description": "cst_…, cus_… or your own id, or { external_id, email, name }"
          },
          "subscription": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "invoice": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "amount": {
            "type": "integer",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "status": {
            "type": "string",
            "enum": [
              "failed",
              "succeeded"
            ]
          },
          "decline_code": {
            "type": "string",
            "maxLength": 255
          },
          "failure_code": {
            "type": "string",
            "maxLength": 255
          },
          "occurred_at": {
            "type": "string",
            "maxLength": 40,
            "description": "ISO 8601"
          },
          "invoice_url": {
            "type": "string",
            "maxLength": 2000
          }
        },
        "description": "Params of record_payment: Record a live payment attempt."
      },
      "LiveRequestCreateRecordPayment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action",
          "params"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "record_payment"
            ]
          },
          "params": {
            "$ref": "#/components/schemas/LiveRequestParamsRecordPayment"
          },
          "reason": {
            "type": "string",
            "maxLength": 1000,
            "description": "Shown to the human as the app's note (untrusted text)."
          }
        },
        "nullable": false
      },
      "LiveRequestCreate": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/LiveRequestCreateCreateLiveApiKey"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreatePublishFlowLive"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateSetBillingSource"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateCreateWebhookEndpoint"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateUpdateWebhookEndpoint"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateDeleteWebhookEndpoint"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateTestWebhookEndpoint"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateCreateAttributeDefinition"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateUpdateAttributeDefinition"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateCreateCustomer"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateUpdateCustomer"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateSetCustomerAttributes"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateRecordUsageEvent"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateRecordUsageEvents"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateCreateSubscription"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateUpdateSubscription"
          },
          {
            "$ref": "#/components/schemas/LiveRequestCreateRecordPayment"
          }
        ],
        "discriminator": {
          "propertyName": "action",
          "mapping": {
            "create_live_api_key": "#/components/schemas/LiveRequestCreateCreateLiveApiKey",
            "publish_flow_live": "#/components/schemas/LiveRequestCreatePublishFlowLive",
            "set_billing_source": "#/components/schemas/LiveRequestCreateSetBillingSource",
            "create_webhook_endpoint": "#/components/schemas/LiveRequestCreateCreateWebhookEndpoint",
            "update_webhook_endpoint": "#/components/schemas/LiveRequestCreateUpdateWebhookEndpoint",
            "delete_webhook_endpoint": "#/components/schemas/LiveRequestCreateDeleteWebhookEndpoint",
            "test_webhook_endpoint": "#/components/schemas/LiveRequestCreateTestWebhookEndpoint",
            "create_attribute_definition": "#/components/schemas/LiveRequestCreateCreateAttributeDefinition",
            "update_attribute_definition": "#/components/schemas/LiveRequestCreateUpdateAttributeDefinition",
            "create_customer": "#/components/schemas/LiveRequestCreateCreateCustomer",
            "update_customer": "#/components/schemas/LiveRequestCreateUpdateCustomer",
            "set_customer_attributes": "#/components/schemas/LiveRequestCreateSetCustomerAttributes",
            "record_usage_event": "#/components/schemas/LiveRequestCreateRecordUsageEvent",
            "record_usage_events": "#/components/schemas/LiveRequestCreateRecordUsageEvents",
            "create_subscription": "#/components/schemas/LiveRequestCreateCreateSubscription",
            "update_subscription": "#/components/schemas/LiveRequestCreateUpdateSubscription",
            "record_payment": "#/components/schemas/LiveRequestCreateRecordPayment"
          }
        }
      },
      "SandboxCustomer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "subscription",
          "payment_method",
          "next"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "sandbox_customer"
            ]
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "subscription": {
            "$ref": "#/components/schemas/Subscription"
          },
          "payment_method": {
            "$ref": "#/components/schemas/PaymentMethod"
          },
          "next": {
            "type": "string"
          }
        },
        "nullable": false
      },
      "SandboxFailedPayment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "invoice",
          "payment",
          "recovery_run",
          "skipped",
          "next"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "sandbox_failed_payment"
            ]
          },
          "customer": {
            "type": "string"
          },
          "invoice": {
            "$ref": "#/components/schemas/Invoice"
          },
          "payment": {
            "$ref": "#/components/schemas/Payment"
          },
          "recovery_run": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "state",
              "holdout",
              "stop_reason"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "holdout": {
                "type": "boolean"
              },
              "stop_reason": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true
          },
          "skipped": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "reason"
            ],
            "properties": {
              "code": {
                "type": "string",
                "nullable": true
              },
              "reason": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true,
            "description": "Why no recovery run started (e.g. not_published)."
          },
          "next": {
            "type": "string"
          }
        },
        "nullable": false
      },
      "SandboxCardRemoved": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "customer",
          "payment_method",
          "flagged",
          "prevention_run"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "sandbox_card_removed"
            ]
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "payment_method": {
            "$ref": "#/components/schemas/PaymentMethod"
          },
          "flagged": {
            "type": "boolean"
          },
          "prevention_run": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "state",
              "holdout",
              "stop_reason"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "holdout": {
                "type": "boolean"
              },
              "stop_reason": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true
          }
        },
        "nullable": false
      },
      "SandboxCancelFlowRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "session"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "sandbox_cancel_flow_run"
            ]
          },
          "session": {
            "$ref": "#/components/schemas/CancelSession"
          }
        },
        "nullable": false
      },
      "IntegrationVerification": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "complete",
          "passed",
          "total",
          "checks",
          "go_live_url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "integration_verification"
            ]
          },
          "livemode": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "complete": {
            "type": "boolean"
          },
          "passed": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "status",
                "detail",
                "fix"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "billing_connected",
                    "webhook_verified",
                    "identify_received",
                    "cancel_flow_opened",
                    "click_to_cancel",
                    "failed_payment_email",
                    "no_secret_in_code"
                  ]
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pass",
                    "fail",
                    "unknown"
                  ],
                  "description": "unknown: NoChurn cannot tell yet (email being written, no code sent)."
                },
                "detail": {
                  "type": "string"
                },
                "fix": {
                  "type": "string",
                  "nullable": true,
                  "description": "What to do next; null when it passes."
                }
              },
              "nullable": false
            }
          },
          "go_live_url": {
            "type": "string",
            "nullable": true,
            "description": "Where a human goes live, once everything passes."
          }
        },
        "nullable": false
      },
      "ProjectDetection": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "platforms",
          "backend",
          "frontend",
          "mobile",
          "billing",
          "plan"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "project_detection"
            ]
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "name",
                "kind",
                "evidence"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "nextjs"
                },
                "name": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "web",
                    "web_front",
                    "backend",
                    "ios",
                    "android",
                    "cross_platform"
                  ]
                },
                "evidence": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "nullable": false
            }
          },
          "backend": {
            "type": "string",
            "nullable": true
          },
          "frontend": {
            "type": "string",
            "nullable": true
          },
          "mobile": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "billing": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "stripe",
              "store_subscriptions"
            ],
            "properties": {
              "stripe": {
                "type": "boolean"
              },
              "store_subscriptions": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "detected",
                  "stores",
                  "evidence",
                  "note"
                ],
                "properties": {
                  "detected": {
                    "type": "boolean"
                  },
                  "stores": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "app_store",
                        "google_play"
                      ]
                    }
                  },
                  "evidence": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "note": {
                    "type": "string",
                    "nullable": true
                  }
                },
                "nullable": false
              }
            },
            "nullable": false
          },
          "plan": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "steps",
              "warnings"
            ],
            "properties": {
              "steps": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "key",
                    "title",
                    "status",
                    "tools",
                    "snippet_cases"
                  ],
                  "properties": {
                    "key": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "available",
                        "not_available_yet"
                      ]
                    },
                    "tools": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "snippet_cases": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "snippet_platform": {
                      "type": "string"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "nullable": false
          }
        },
        "nullable": false
      },
      "IntegrationSnippets": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "version",
          "platform",
          "language",
          "api_base_url",
          "snippets"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "integration_snippets"
            ]
          },
          "version": {
            "type": "string",
            "example": "2026-11-06.2"
          },
          "platform": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string",
            "nullable": true
          },
          "api_base_url": {
            "type": "string"
          },
          "snippets": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "case",
                "status",
                "title",
                "language",
                "code"
              ],
              "properties": {
                "case": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "available",
                    "not_available_yet"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "language": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "node",
                    "ruby",
                    "python",
                    "curl",
                    "browser",
                    null
                  ]
                },
                "label": {
                  "type": "string"
                },
                "syntax": {
                  "type": "string"
                },
                "runs_on": {
                  "type": "string",
                  "enum": [
                    "server",
                    "browser"
                  ]
                },
                "insert": {
                  "type": "string",
                  "description": "Where to put it in the project."
                },
                "env": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Environment variables the code reads."
                },
                "file": {
                  "type": "string"
                },
                "code": {
                  "type": "string",
                  "nullable": true
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        },
        "nullable": false
      },
      "ClientVersions": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "livemode",
          "window_days",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "client_versions"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "window_days": {
            "type": "integer",
            "example": 90
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "object",
                "client",
                "name",
                "latest",
                "minimum",
                "status",
                "versions"
              ],
              "properties": {
                "object": {
                  "type": "string",
                  "enum": [
                    "client"
                  ]
                },
                "client": {
                  "type": "string",
                  "example": "widget"
                },
                "name": {
                  "type": "string",
                  "nullable": true,
                  "example": "Cancel widget (js.nochurn.sh)"
                },
                "latest": {
                  "type": "string",
                  "nullable": true,
                  "example": "1.0.0",
                  "description": "null for a client NoChurn does not publish."
                },
                "minimum": {
                  "type": "string",
                  "nullable": true,
                  "example": "1.0.0",
                  "description": "Older versions are unsupported."
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "current",
                    "update_available",
                    "unsupported",
                    "unknown"
                  ],
                  "description": "The worst status among the versions seen in the last 7 days, else the last version's."
                },
                "versions": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "id",
                      "version",
                      "status",
                      "origin_host",
                      "api_key",
                      "oauth_client",
                      "first_seen_at",
                      "last_seen_at",
                      "calls_count"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "clv_4fJ8sK2pQ9wLm3Xz"
                      },
                      "version": {
                        "type": "string",
                        "example": "1.0.0"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "current",
                          "update_available",
                          "unsupported",
                          "unknown"
                        ],
                        "description": "current; update_available (below latest); unsupported (below minimum); unknown (a client NoChurn does not publish)."
                      },
                      "origin_host": {
                        "type": "string",
                        "nullable": true,
                        "example": "app.acme.com",
                        "description": "Widget only: the host of the page's Origin (never a path)."
                      },
                      "api_key": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "id",
                          "name",
                          "kind",
                          "mode",
                          "last4"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string",
                            "nullable": true
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "secret",
                              "publishable"
                            ]
                          },
                          "mode": {
                            "type": "string",
                            "enum": [
                              "test",
                              "live"
                            ]
                          },
                          "last4": {
                            "type": "string"
                          }
                        },
                        "nullable": true
                      },
                      "oauth_client": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "id",
                          "name"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        },
                        "nullable": true,
                        "description": "The AI app, for the MCP server through OAuth."
                      },
                      "first_seen_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "last_seen_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "calls_count": {
                        "type": "integer",
                        "description": "Approximate (a lower bound): written at most once every 5 minutes per row."
                      }
                    },
                    "nullable": false
                  },
                  "description": "Newest first."
                }
              },
              "nullable": false
            }
          }
        },
        "nullable": false
      },
      "LogEntryDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "livemode",
          "occurred_at",
          "source",
          "type",
          "customer",
          "summary",
          "status",
          "error",
          "why",
          "ref",
          "request_id",
          "payload"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "log_entry"
            ]
          },
          "id": {
            "type": "string",
            "example": "log_3kH9sQ2pVb7LmXz1"
          },
          "livemode": {
            "type": "boolean"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "Millisecond precision."
          },
          "source": {
            "type": "string",
            "enum": [
              "stripe",
              "api",
              "nochurn",
              "widget",
              "webhook",
              "chargebee",
              "recurly",
              "maxio",
              "zuora",
              "paddle",
              "lemon_squeezy",
              "polar",
              "braintree",
              "mollie",
              "gocardless",
              "razorpay",
              "app_store",
              "google_play",
              "custom",
              "intercom",
              "crisp",
              "zendesk",
              "hubspot",
              "helpscout",
              "gorgias"
            ],
            "description": "stripe (webhooks), api (your calls with an API key), nochurn (decisions), widget, webhook (outgoing), a billing tool (chargebee, zuora…: its webhooks and what NoChurn reads from it), a support tool (intercom, crisp, zendesk, hubspot, helpscout, gorgias)."
          },
          "type": {
            "type": "string",
            "example": "invoice.payment_failed"
          },
          "customer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "cst_4fJ8sK2pQ9wLm3Xz"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true,
            "description": "The customer this line is about (open it with /v1/customers/{id})."
          },
          "summary": {
            "type": "string",
            "nullable": true,
            "example": "$49.00 · insufficient_funds"
          },
          "status": {
            "type": "string",
            "example": "received",
            "description": "HTTP status (\"200\", \"500\") or ok, received, ignored, failed."
          },
          "error": {
            "type": "boolean"
          },
          "why": {
            "type": "string",
            "nullable": true,
            "description": "Why this happened (the reason of the NoChurn decision)."
          },
          "ref": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "example": "event"
              },
              "id": {
                "type": "string",
                "example": "nevt_tZCYqtEDVAvwZjD5"
              }
            },
            "nullable": true
          },
          "request_id": {
            "type": "string",
            "nullable": true
          },
          "payload": {
            "type": "object",
            "additionalProperties": true,
            "description": "Secrets, tokens and emails are filtered."
          }
        },
        "nullable": false
      },
      "ApiKeyList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "EventList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "StripeConnectionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StripeConnection"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "CustomerDuplicateList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerDuplicate"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "SubscriptionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscription"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "InvoiceList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Invoice"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "PaymentMethodList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentMethod"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "MembershipList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Membership"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "InvitationList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Invitation"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "FlowList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Flow"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "FlowVersionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlowVersion"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "FlowRunList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlowRun"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "SendingDomainList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SendingDomain"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "MessageList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "EmailSuppressionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailSuppression"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "WebhookEndpointList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "WebhookDeliveryList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "PaymentList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Payment"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "OfferList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Offer"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "ReviewPlatformList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReviewPlatform"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "SendScheduleFlowOverrideList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SendScheduleFlowOverride"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "AttributeDefinitionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttributeDefinition"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "ImportList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Import"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "SegmentList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Segment"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "SegmentFieldList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SegmentField"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "ExperimentList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Experiment"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "CancelSessionReportList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CancelSessionReport"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "IntegrationList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Integration"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "QuerySourceList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuerySource"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "SavedQueryList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SavedQuery"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "BillingConnectionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingConnection"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "BillingProviderList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingProvider"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "DisputeList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Dispute"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "FraudWarningList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FraudWarning"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "OauthGrantList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OauthGrant"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      },
      "LiveRequestList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LiveRequest"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as starting_after to get the next page."
          }
        },
        "nullable": false
      }
    }
  },
  "paths": {
    "/v1/account": {
      "get": {
        "summary": "The account of the API key (or of the dashboard session)",
        "tags": [
          "Account"
        ],
        "operationId": "getAccount",
        "responses": {
          "200": {
            "description": "account",
            "content": {
              "application/json": {
                "example": {
                  "object": "account",
                  "id": "acc_pApr4brLkk4KGSjf",
                  "name": "Account 1",
                  "billing_source": "stripe",
                  "sandbox": true,
                  "default_locale": "en",
                  "plan": "free",
                  "external_id_metadata_key": "user_id",
                  "require_two_factor": false,
                  "timezone": "Europe/Paris",
                  "audience": null,
                  "reporting_currency": null,
                  "onboarding": {
                    "step": "billing",
                    "skipped": [],
                    "completed_at": null
                  },
                  "disputes_settings": {
                    "auto_submit": false,
                    "auto_submit_hours_before": 24,
                    "auto_refund_fraud_warnings": false,
                    "auto_refund_max_cents": null,
                    "cancel_subscription_on_fraud_refund": true,
                    "notify_email": true,
                    "notify_slack": false,
                    "contest_cost_cents": null,
                    "contest_cost_currency": null,
                    "auto_submit_min_score": 70
                  },
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "description": "missing key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Update account settings",
        "tags": [
          "Account"
        ],
        "operationId": "updateAccount",
        "parameters": [],
        "responses": {
          "200": {
            "description": "amounts reported in another currency",
            "content": {
              "application/json": {
                "example": {
                  "object": "account",
                  "id": "acc_hM7usMgmWPctiKMY",
                  "name": "Renamed",
                  "billing_source": "stripe",
                  "sandbox": true,
                  "default_locale": "en",
                  "plan": "free",
                  "external_id_metadata_key": "app_user_id",
                  "require_two_factor": false,
                  "timezone": "Europe/Paris",
                  "audience": null,
                  "reporting_currency": null,
                  "onboarding": {
                    "step": "billing",
                    "skipped": [],
                    "completed_at": null
                  },
                  "disputes_settings": {
                    "auto_submit": false,
                    "auto_submit_hours_before": 24,
                    "auto_refund_fraud_warnings": false,
                    "auto_refund_max_cents": null,
                    "cancel_subscription_on_fraud_refund": true,
                    "notify_email": true,
                    "notify_slack": false,
                    "contest_cost_cents": null,
                    "contest_cost_currency": null,
                    "auto_submit_min_score": 70
                  },
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "400": {
            "description": "a step that cannot be skipped",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "unknown time zone",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "default_locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "de",
                      "fr",
                      "it",
                      "pt"
                    ]
                  },
                  "billing_source": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "api"
                    ]
                  },
                  "external_id_metadata_key": {
                    "type": "string"
                  },
                  "require_two_factor": {
                    "type": "boolean",
                    "description": "Owners only, from a dashboard session with 2FA on."
                  },
                  "timezone": {
                    "type": "string",
                    "example": "Europe/Paris",
                    "description": "IANA time zone."
                  },
                  "reporting_currency": {
                    "type": "string",
                    "nullable": true,
                    "example": "usd",
                    "description": "Currency every amount is converted into (one with ECB rates). null: Stripe's default currency."
                  },
                  "onboarding": {
                    "type": "object",
                    "description": "Where the onboarding resumes. completed_at is set when step becomes done.",
                    "properties": {
                      "step": {
                        "type": "string",
                        "enum": [
                          "billing",
                          "support",
                          "cancel_modal",
                          "email_domain",
                          "done"
                        ]
                      },
                      "skipped": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "enum": [
                            "support",
                            "cancel_modal",
                            "email_domain"
                          ]
                        }
                      }
                    }
                  },
                  "disputes_settings": {
                    "type": "object",
                    "description": "Chargebacks › Settings (owner / admin). Only the keys sent change.",
                    "properties": {
                      "auto_submit": {
                        "type": "boolean"
                      },
                      "auto_submit_hours_before": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 168
                      },
                      "auto_refund_fraud_warnings": {
                        "type": "boolean"
                      },
                      "auto_refund_max_cents": {
                        "type": "integer",
                        "nullable": true
                      },
                      "cancel_subscription_on_fraud_refund": {
                        "type": "boolean"
                      },
                      "notify_email": {
                        "type": "boolean"
                      },
                      "notify_slack": {
                        "type": "boolean"
                      },
                      "contest_cost_cents": {
                        "type": "integer",
                        "nullable": true
                      },
                      "contest_cost_currency": {
                        "type": "string",
                        "nullable": true
                      },
                      "auto_submit_min_score": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      }
                    }
                  }
                }
              },
              "example": {
                "name": "Renamed",
                "external_id_metadata_key": "app_user_id"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/sandbox/customers": {
      "post": {
        "summary": "Sandbox: create a test customer",
        "tags": [
          "Sandbox"
        ],
        "operationId": "createSandboxCustomer",
        "description": "Test mode only. A customer with an active subscription billed by API (no Stripe call) renewing in renews_in_days (5), and a default test card (visa 4242). Writes sandbox.customer_created.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "created",
            "content": {
              "application/json": {
                "example": {
                  "object": "sandbox_customer",
                  "customer": {
                    "object": "customer",
                    "id": "cst_bunoEakFaTiPtdny",
                    "stripe_customer_id": null,
                    "external_id": "sandbox_204d2c04",
                    "stripe_account": null,
                    "livemode": false,
                    "email": "sandbox+204d2c04@example.com",
                    "company_domain": null,
                    "name": "Sandbox customer",
                    "locale": null,
                    "country": null,
                    "region": null,
                    "timezone": "Europe/Paris",
                    "timezone_source": "account",
                    "attributes": {
                      "signup_source": "ads"
                    },
                    "churn_score": null,
                    "churn_level": null,
                    "flag": null,
                    "state": "active",
                    "plan": "Team",
                    "mrr_cents": 4900,
                    "lifetime_value_cents": 0,
                    "currency": "usd",
                    "payment_method": {
                      "id": "npm_wv5NxAxFfWoidwEv",
                      "type": "card",
                      "brand": "visa",
                      "last4": "4242",
                      "exp_month": 12,
                      "exp_year": 2029,
                      "funding": "credit",
                      "wallet": null,
                      "risk_category": null,
                      "label": "Visa •••• 4242"
                    },
                    "last_seen_at": null,
                    "churned_at": null,
                    "flows_paused_at": null,
                    "signup_at": "2026-11-04T10:00:00.000Z",
                    "created_at": "2026-11-04T10:00:00.000Z",
                    "updated_at": "2026-11-04T10:00:00.000Z",
                    "identifiers": [
                      {
                        "kind": "email",
                        "provider": null,
                        "connection": null,
                        "value": "sandbox+204d2c04@example.com",
                        "source": "api",
                        "created_at": "2026-11-04T10:00:00.000Z"
                      },
                      {
                        "kind": "external",
                        "provider": null,
                        "connection": null,
                        "value": "sandbox_204d2c04",
                        "source": "api",
                        "created_at": "2026-11-04T10:00:00.000Z"
                      }
                    ]
                  },
                  "subscription": {
                    "object": "subscription",
                    "id": "nsub_ee6FgdZrth7eEGKc",
                    "customer": "cst_bunoEakFaTiPtdny",
                    "stripe_subscription_id": null,
                    "external_id": "sandbox_sub_204d2c04",
                    "livemode": false,
                    "status": "active",
                    "plan": "Team",
                    "amount_cents": 4900,
                    "currency": "usd",
                    "interval": "month",
                    "interval_count": 1,
                    "mrr_cents": 4900,
                    "current_period_end": "2026-11-09T10:00:00.000Z",
                    "cancel_at_period_end": false,
                    "canceled_at": null,
                    "paused_until": null,
                    "trial_end": null,
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "payment_method": {
                    "object": "payment_method",
                    "id": "npm_wv5NxAxFfWoidwEv",
                    "customer": "cst_bunoEakFaTiPtdny",
                    "stripe_payment_method_id": null,
                    "livemode": false,
                    "type": "card",
                    "brand": "visa",
                    "last4": "4242",
                    "exp_month": 12,
                    "exp_year": 2029,
                    "funding": "credit",
                    "country": null,
                    "wallet": null,
                    "is_default": true,
                    "detached_at": null,
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "next": "Play a scenario: POST /v1/sandbox/failed_payment, /card_removed or /cancel_flow_run with customer: sandbox_204d2c04."
                },
                "schema": {
                  "$ref": "#/components/schemas/SandboxCustomer"
                }
              }
            }
          },
          "403": {
            "description": "live mode",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "external_id": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "locale": {
                    "type": "string"
                  },
                  "plan": {
                    "type": "string",
                    "example": "Pro"
                  },
                  "amount": {
                    "type": "integer",
                    "example": 4900,
                    "description": "Cents."
                  },
                  "currency": {
                    "type": "string",
                    "example": "usd"
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "month",
                      "year"
                    ]
                  },
                  "renews_in_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365
                  },
                  "attributes": {
                    "type": "object",
                    "additionalProperties": true,
                    "example": {
                      "signup_source": "ads"
                    }
                  }
                }
              },
              "example": {
                "plan": "Team",
                "attributes": {
                  "signup_source": "ads"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sandbox/failed_payment": {
      "post": {
        "summary": "Sandbox: simulate a failed payment",
        "tags": [
          "Sandbox"
        ],
        "operationId": "simulateFailedPayment",
        "description": "Test mode only. An open invoice and a failed payment, then the recovery flow like a failure sent by API: the day-0 email is written in the background (never sent to a real inbox). invoice_url is the card update page of your app (billing by API); without it the email waits for review without a card link.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "recovery run started",
            "content": {
              "application/json": {
                "example": {
                  "object": "sandbox_failed_payment",
                  "customer": "cst_bKA5uydA3TFbny8p",
                  "invoice": {
                    "object": "invoice",
                    "id": "ninv_r3bxXGhL1pmwzzsq",
                    "customer": "cst_bKA5uydA3TFbny8p",
                    "subscription": "nsub_h4fGLF2jw3Fmj949",
                    "stripe_invoice_id": null,
                    "external_id": "sandbox_in_5c71a02c",
                    "livemode": false,
                    "number": null,
                    "status": "open",
                    "amount_due_cents": 4900,
                    "amount_paid_cents": 0,
                    "amount_remaining_cents": 4900,
                    "currency": "usd",
                    "attempt_count": 1,
                    "next_attempt_at": null,
                    "failure_code": null,
                    "decline_code": "insufficient_funds",
                    "hosted_invoice_url": "https://app.example.com/billing",
                    "paid_at": null,
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "payment": {
                    "object": "payment",
                    "id": "npay_36FrWRqwQscjZzbo",
                    "livemode": false,
                    "customer": "cst_bKA5uydA3TFbny8p",
                    "subscription": "nsub_h4fGLF2jw3Fmj949",
                    "invoice": "ninv_r3bxXGhL1pmwzzsq",
                    "external_id": "sandbox_pay_5c71a02c",
                    "stripe_charge_id": null,
                    "status": "failed",
                    "amount_cents": 4900,
                    "currency": "usd",
                    "decline_code": "insufficient_funds",
                    "failure_code": null,
                    "occurred_at": "2026-11-04T10:00:00Z",
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "recovery_run": {
                    "id": "run_mmJr34RXHjDV6MZi",
                    "state": "active",
                    "holdout": false,
                    "stop_reason": null
                  },
                  "skipped": null,
                  "next": "The day-0 email is written in the background (test mode: never sent to a real inbox). Check it with GET /v1/integration/verify."
                },
                "schema": {
                  "$ref": "#/components/schemas/SandboxFailedPayment"
                }
              }
            }
          },
          "404": {
            "description": "unknown customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer"
                ],
                "properties": {
                  "customer": {
                    "type": "string",
                    "description": "cst_… or your own id."
                  },
                  "amount": {
                    "type": "integer"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "decline_code": {
                    "type": "string",
                    "example": "insufficient_funds"
                  },
                  "invoice_url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              },
              "example": {
                "customer": "cst_bKA5uydA3TFbny8p",
                "invoice_url": "https://app.example.com/billing"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/sandbox/card_removed": {
      "post": {
        "summary": "Sandbox: the customer removes their card before the renewal",
        "tags": [
          "Sandbox"
        ],
        "operationId": "simulateCardRemoved",
        "description": "Test mode only, on a sandbox card. Prevention signal card_removed: the customer is flagged, and the prevention flow writes its reminder when it is published in test mode.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "flagged",
            "content": {
              "application/json": {
                "example": {
                  "object": "sandbox_card_removed",
                  "customer": {
                    "object": "customer",
                    "id": "cst_m7f9kZH5XcRMAx7x",
                    "stripe_customer_id": null,
                    "external_id": "sandbox_a096134f",
                    "stripe_account": null,
                    "livemode": false,
                    "email": "sandbox+a096134f@example.com",
                    "company_domain": null,
                    "name": "Sandbox customer",
                    "locale": null,
                    "country": null,
                    "region": null,
                    "timezone": "Europe/Paris",
                    "timezone_source": "account",
                    "attributes": {},
                    "churn_score": 60,
                    "churn_level": "medium",
                    "flag": {
                      "reason": "card_removed",
                      "flagged_at": "2026-11-04T10:00:00.000Z"
                    },
                    "state": "active",
                    "plan": "Pro",
                    "mrr_cents": 4900,
                    "lifetime_value_cents": 0,
                    "currency": "usd",
                    "payment_method": null,
                    "last_seen_at": null,
                    "churned_at": null,
                    "flows_paused_at": null,
                    "signup_at": "2026-11-04T10:00:00.000Z",
                    "created_at": "2026-11-04T10:00:00.000Z",
                    "updated_at": "2026-11-04T10:00:00.000Z",
                    "identifiers": [
                      {
                        "kind": "email",
                        "provider": null,
                        "connection": null,
                        "value": "sandbox+a096134f@example.com",
                        "source": "api",
                        "created_at": "2026-11-04T10:00:00.000Z"
                      },
                      {
                        "kind": "external",
                        "provider": null,
                        "connection": null,
                        "value": "sandbox_a096134f",
                        "source": "api",
                        "created_at": "2026-11-04T10:00:00.000Z"
                      }
                    ]
                  },
                  "payment_method": {
                    "object": "payment_method",
                    "id": "npm_r3X9MXd9dt36G7ZR",
                    "customer": "cst_m7f9kZH5XcRMAx7x",
                    "stripe_payment_method_id": null,
                    "livemode": false,
                    "type": "card",
                    "brand": "visa",
                    "last4": "4242",
                    "exp_month": 12,
                    "exp_year": 2029,
                    "funding": "credit",
                    "country": null,
                    "wallet": null,
                    "is_default": false,
                    "detached_at": "2026-11-04T10:00:00.000Z",
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "flagged": true,
                  "prevention_run": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SandboxCardRemoved"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer"
                ],
                "properties": {
                  "customer": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "customer": "cst_m7f9kZH5XcRMAx7x"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/sandbox/cancel_flow_run": {
      "post": {
        "summary": "Sandbox: play the cancel flow",
        "tags": [
          "Sandbox"
        ],
        "operationId": "runSandboxCancelFlow",
        "description": "Test mode only. Opens the cancel flow, answers a reason (the first one by default), refuses or accepts the offer, then cancels in one click (click to cancel). Billing by API: the cancellation reaches your app as billing.cancel_requested.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "cancelled",
            "content": {
              "application/json": {
                "example": {
                  "object": "sandbox_cancel_flow_run",
                  "session": {
                    "object": "cancel_session",
                    "id": "ses_E4ydMSMHe2opxRar",
                    "livemode": false,
                    "step": "done",
                    "outcome": "cancelled",
                    "locale": "en",
                    "holdout": false,
                    "published": false,
                    "customer": {
                      "id": "cst_vyBRXyK4QwPYf9mu",
                      "stripe_customer_id": null,
                      "external_id": "sandbox_474b675f"
                    },
                    "subscription": {
                      "id": "nsub_G5GU7sVoHJFgxgQc",
                      "plan": "Pro",
                      "status": "active",
                      "amount_cents": 4900,
                      "currency": "usd",
                      "interval": "month",
                      "interval_count": 1,
                      "current_period_end": "2026-11-09T10:00:00Z",
                      "cancel_at_period_end": false
                    },
                    "subscriptions": [],
                    "reasons": [
                      {
                        "key": "too_expensive",
                        "label": "It's too expensive",
                        "free_text": false
                      },
                      {
                        "key": "missing_feature",
                        "label": "A feature is missing",
                        "free_text": false
                      },
                      {
                        "key": "not_using",
                        "label": "I don't use it enough",
                        "free_text": false
                      },
                      {
                        "key": "switching",
                        "label": "I'm switching to another tool",
                        "free_text": false
                      },
                      {
                        "key": "temporary",
                        "label": "I only need a break",
                        "free_text": false
                      },
                      {
                        "key": "project_finished",
                        "label": "My project is finished",
                        "free_text": false
                      },
                      {
                        "key": "other",
                        "label": "Something else",
                        "free_text": true
                      }
                    ],
                    "offer": null,
                    "talk_url": null,
                    "texts": {
                      "review_ask": "Would you leave a short review on {{platform}}? No reward, just your honest opinion.",
                      "review_cta": "Leave a review",
                      "title": "Before you go",
                      "reason_prompt": "Why are you cancelling?",
                      "other_placeholder": "Tell us more (optional)",
                      "offer_title": "One last thing",
                      "accept": "Accept the offer",
                      "decline": "No thanks, continue",
                      "feedback_prompt": "Anything we could have done better?",
                      "feedback_submit": "Continue",
                      "confirm_title": "Cancel your subscription?",
                      "confirm_body": "You keep access until the end of the paid period.",
                      "cancel_now": "Cancel my subscription",
                      "saved_title": "Thanks for staying!",
                      "cancelled_title": "Your subscription is cancelled",
                      "cancelled_body": "You keep access until the end of the paid period.",
                      "offer_discount": "{{percent}}% off for {{months}} months",
                      "offer_pause": "Pause for {{months}} months, free",
                      "offer_downgrade": "Switch to {{plan}}",
                      "offer_talk": "Talk to the team",
                      "choose_subscription_title": "Which subscription?",
                      "choose_subscription_body": "You have several subscriptions. Choose the one you want to cancel.",
                      "choose_subscription_select": "Continue with this one",
                      "cancel_this_one": "Cancel this one",
                      "renews_on": "Renews on {{date}}"
                    },
                    "feedback_enabled": true,
                    "feedback": {
                      "enabled": true,
                      "required": false
                    },
                    "theme": {
                      "object": "cancel_flow_theme",
                      "preset": "light",
                      "format": "modal",
                      "tokens": {
                        "bg": "#f5f6fa",
                        "surface": "#ffffff",
                        "text": "#1c1e4a",
                        "muted": "#686b85",
                        "accent": "#4637d9",
                        "accent_text": "#ffffff",
                        "border": "#e3e5ec",
                        "danger": "#c2410c",
                        "font": "system-ui, sans-serif",
                        "font_size": 15,
                        "radius": 12,
                        "radius_control": 8,
                        "overlay": 0.5,
                        "max_width": 480,
                        "shadow": "md",
                        "spacing": "regular",
                        "border_width": 1,
                        "border_style": "solid",
                        "font_scale": 1.25,
                        "font_weight_title": 600
                      },
                      "font_url": null,
                      "brand": "logo",
                      "logo_url": null,
                      "app_name": "Account 1",
                      "end_screen": {
                        "style": "celebrate",
                        "auto_close_seconds": 6
                      },
                      "custom_css": "",
                      "cancel_button_check": "ok",
                      "classes": [
                        "nc-modal",
                        "nc-title",
                        "nc-reason",
                        "nc-offer",
                        "nc-btn-primary",
                        "nc-btn-cancel",
                        "nc-footer"
                      ]
                    },
                    "cancel_url_now": null,
                    "cancel_status": "requested",
                    "cancel_effective_at": "2026-11-09T10:00:00Z",
                    "result": null,
                    "access_until": "2026-11-09T10:00:00Z",
                    "confirmation_email_sent": false,
                    "review": null,
                    "expires_at": "2026-11-05T10:00:00Z",
                    "created_at": "2026-11-04T10:00:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/SandboxCancelFlowRun"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer"
                ],
                "properties": {
                  "customer": {
                    "type": "string"
                  },
                  "subscription": {
                    "type": "string",
                    "description": "Several live subscriptions: the one to play (default: the first listed)."
                  },
                  "reason": {
                    "type": "string"
                  },
                  "accept_offer": {
                    "type": "boolean"
                  },
                  "locale": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "customer": "cst_vyBRXyK4QwPYf9mu"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/integration/verify": {
      "get": {
        "summary": "Integration checklist",
        "tags": [
          "Agent integration"
        ],
        "operationId": "verifyIntegration",
        "description": "The 7 points of BRIEF §5.9.4, read from the account's test-mode data: pass, fail (with its fix) or unknown. Sandbox runs never count as your app's work for billing_connected and cancel_flow_opened.",
        "responses": {
          "200": {
            "description": "the checklist",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration_verification",
                  "livemode": false,
                  "complete": false,
                  "passed": 0,
                  "total": 7,
                  "checks": [
                    {
                      "key": "billing_connected",
                      "status": "fail",
                      "detail": "No billing connected and no customer received from your app yet.",
                      "fix": "Send a customer (POST /v1/customers) and its subscription (POST /v1/subscriptions) from your backend with your test key."
                    },
                    {
                      "key": "webhook_verified",
                      "status": "fail",
                      "detail": "No webhook endpoint in test mode.",
                      "fix": "Create one (webhook_endpoints_create) on a route of your backend that checks NoChurn-Signature (integration_get_snippets case webhooks)."
                    },
                    {
                      "key": "identify_received",
                      "status": "fail",
                      "detail": "No attribute received yet.",
                      "fix": "From your backend, send signup_source and onboarding_done (and the attributes of your app) when a user signs up or changes: POST /v1/customers/{id}/attributes (integration_get_snippets case identify)."
                    },
                    {
                      "key": "cancel_flow_opened",
                      "status": "fail",
                      "detail": "Your app has not opened the cancel flow yet (sandbox runs do not count).",
                      "fix": "Wire your cancel button: your backend signs a customer token and your app starts POST /v1/cancel_sessions (integration_get_snippets case signed_token; the cancel.js modal is not released yet), then click it once in test mode."
                    },
                    {
                      "key": "click_to_cancel",
                      "status": "fail",
                      "detail": "No cancellation in test mode yet.",
                      "fix": "Cancel once in test mode from your app (POST /v1/cancel_sessions/{id}/cancel), or play it with sandbox_run_cancel_flow."
                    },
                    {
                      "key": "failed_payment_email",
                      "status": "fail",
                      "detail": "No failed payment in test mode yet.",
                      "fix": "Create a sandbox customer (sandbox_create_customer), then sandbox_simulate_failed_payment."
                    },
                    {
                      "key": "no_secret_in_code",
                      "status": "unknown",
                      "detail": "No code sent.",
                      "fix": "Call integration_verify with snippets: the files that call NoChurn or hold configuration (front end and back end)."
                    }
                  ],
                  "go_live_url": null
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegrationVerification"
                }
              }
            }
          },
          "403": {
            "description": "live mode",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Integration checklist, with the code to scan for secrets",
        "tags": [
          "Agent integration"
        ],
        "operationId": "verifyIntegrationWithCode",
        "description": "Same checklist; no_secret_in_code scans the files sent (secret keys nc_, sk_/rk_, whsec_, ncws_). Nothing is stored: findings only carry the file, the line and a masked preview.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "the checklist",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration_verification",
                  "livemode": false,
                  "complete": false,
                  "passed": 0,
                  "total": 7,
                  "checks": [
                    {
                      "key": "billing_connected",
                      "status": "fail",
                      "detail": "No billing connected and no customer received from your app yet.",
                      "fix": "Send a customer (POST /v1/customers) and its subscription (POST /v1/subscriptions) from your backend with your test key."
                    },
                    {
                      "key": "webhook_verified",
                      "status": "fail",
                      "detail": "No webhook endpoint in test mode.",
                      "fix": "Create one (webhook_endpoints_create) on a route of your backend that checks NoChurn-Signature (integration_get_snippets case webhooks)."
                    },
                    {
                      "key": "identify_received",
                      "status": "fail",
                      "detail": "No attribute received yet.",
                      "fix": "From your backend, send signup_source and onboarding_done (and the attributes of your app) when a user signs up or changes: POST /v1/customers/{id}/attributes (integration_get_snippets case identify)."
                    },
                    {
                      "key": "cancel_flow_opened",
                      "status": "fail",
                      "detail": "Your app has not opened the cancel flow yet (sandbox runs do not count).",
                      "fix": "Wire your cancel button: your backend signs a customer token and your app starts POST /v1/cancel_sessions (integration_get_snippets case signed_token; the cancel.js modal is not released yet), then click it once in test mode."
                    },
                    {
                      "key": "click_to_cancel",
                      "status": "fail",
                      "detail": "No cancellation in test mode yet.",
                      "fix": "Cancel once in test mode from your app (POST /v1/cancel_sessions/{id}/cancel), or play it with sandbox_run_cancel_flow."
                    },
                    {
                      "key": "failed_payment_email",
                      "status": "fail",
                      "detail": "No failed payment in test mode yet.",
                      "fix": "Create a sandbox customer (sandbox_create_customer), then sandbox_simulate_failed_payment."
                    },
                    {
                      "key": "no_secret_in_code",
                      "status": "fail",
                      "detail": "Secrets found: src/app.js:1 nochurn_live_secret_key (nc_live_…ZZ).",
                      "fix": "Move them to environment variables read by your backend only (e.g. process.env.NOCHURN_API_KEY), never in the app or the front end; revoke a key that was committed."
                    }
                  ],
                  "go_live_url": null
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegrationVerification"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snippets"
                ],
                "properties": {
                  "snippets": {
                    "type": "array",
                    "maxItems": 200,
                    "items": {
                      "type": "object",
                      "required": [
                        "content"
                      ],
                      "properties": {
                        "path": {
                          "type": "string"
                        },
                        "content": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "snippets": [
                  {
                    "path": "src/app.js",
                    "content": "const k = 'nc_live_EXAMPLE00000000000000000'"
                  }
                ]
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/integration/detect": {
      "post": {
        "summary": "Detect the project's platforms and plan the integration",
        "tags": [
          "Agent integration"
        ],
        "operationId": "detectProject",
        "description": "The agent sends file paths, dependency names and the dependency manifests; nothing is stored. Flags App Store / Google Play subscriptions, which NoChurn cannot cancel server side.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "platforms and plan",
            "content": {
              "application/json": {
                "example": {
                  "object": "project_detection",
                  "platforms": [
                    {
                      "key": "nextjs",
                      "name": "Next.js",
                      "kind": "web",
                      "evidence": [
                        "dependency next",
                        "file next.config.mjs"
                      ]
                    }
                  ],
                  "backend": "nextjs",
                  "frontend": "nextjs",
                  "mobile": [],
                  "billing": {
                    "stripe": true,
                    "store_subscriptions": {
                      "detected": false,
                      "stores": [],
                      "evidence": [],
                      "note": null
                    }
                  },
                  "plan": {
                    "steps": [
                      {
                        "key": "setup",
                        "title": "Read what is installed and what is missing",
                        "status": "available",
                        "tools": [
                          "account_get_setup"
                        ],
                        "snippet_cases": []
                      },
                      {
                        "key": "billing",
                        "title": "Connect Stripe in test mode (a human clicks the link)",
                        "status": "available",
                        "tools": [
                          "account_connect_stripe_link"
                        ],
                        "snippet_cases": []
                      },
                      {
                        "key": "keys",
                        "title": "Create a test secret key for the backend (env NOCHURN_API_KEY) and get the publishable key",
                        "status": "available",
                        "tools": [
                          "api_keys_create_test",
                          "publishable_key_get"
                        ],
                        "snippet_cases": []
                      },
                      {
                        "key": "identify",
                        "title": "Send customers and their attributes (identify) and usage events (track)",
                        "status": "available",
                        "tools": [
                          "attributes_define",
                          "integration_get_snippets"
                        ],
                        "snippet_cases": [
                          "identify",
                          "track"
                        ],
                        "snippet_platform": "nextjs"
                      },
                      {
                        "key": "webhooks",
                        "title": "Receive NoChurn's webhooks on a route that checks the signature",
                        "status": "available",
                        "tools": [
                          "webhook_endpoints_create",
                          "integration_get_snippets",
                          "webhook_endpoints_send_test"
                        ],
                        "snippet_cases": [
                          "webhooks"
                        ],
                        "snippet_platform": "nextjs"
                      },
                      {
                        "key": "cancel_flow",
                        "title": "Configure the cancel flow (reasons, offers, theme from the project's colours) and publish it in test mode",
                        "status": "available",
                        "tools": [
                          "cancel_flow_get",
                          "cancel_flow_update_draft",
                          "cancel_flow_set_theme",
                          "flow_publish_test",
                          "cancel_flow_preview_url"
                        ],
                        "snippet_cases": []
                      },
                      {
                        "key": "customer_token",
                        "title": "Sign the customer token on your backend (widget secret, env NOCHURN_WIDGET_SECRET)",
                        "status": "available",
                        "tools": [
                          "integration_get_snippets"
                        ],
                        "snippet_cases": [
                          "signed_token"
                        ],
                        "snippet_platform": "nextjs"
                      },
                      {
                        "key": "cancel_button_web",
                        "title": "Wire your cancel button: headless cancel flow (the cancel.js modal is not released yet)",
                        "status": "available",
                        "tools": [
                          "integration_get_snippets"
                        ],
                        "snippet_cases": [
                          "headless_cancel"
                        ],
                        "snippet_platform": "web"
                      },
                      {
                        "key": "access_check",
                        "title": "Block your app in the backend while a payment fails (GET /v1/customers/{id}/access): the widget's wall alone can be bypassed",
                        "status": "available",
                        "tools": [
                          "integration_get_snippets"
                        ],
                        "snippet_cases": [
                          "access_check"
                        ],
                        "snippet_platform": "nextjs"
                      },
                      {
                        "key": "sandbox",
                        "title": "Play the scenarios end to end in test mode",
                        "status": "available",
                        "tools": [
                          "sandbox_create_customer",
                          "sandbox_simulate_failed_payment",
                          "sandbox_simulate_card_removed",
                          "sandbox_run_cancel_flow"
                        ],
                        "snippet_cases": []
                      },
                      {
                        "key": "verify",
                        "title": "Run the checklist until everything is green, sending the code you changed",
                        "status": "available",
                        "tools": [
                          "integration_verify"
                        ],
                        "snippet_cases": []
                      },
                      {
                        "key": "go_live",
                        "title": "Hand over to a human: going live (live keys, live publication) is approved in the dashboard",
                        "status": "available",
                        "tools": [
                          "request_go_live"
                        ],
                        "snippet_cases": []
                      }
                    ],
                    "warnings": []
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ProjectDetection"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 5000
                  },
                  "dependencies": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "manifests": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "path",
                        "content"
                      ],
                      "properties": {
                        "path": {
                          "type": "string"
                        },
                        "content": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "files": [
                  "next.config.mjs"
                ],
                "manifests": [
                  {
                    "path": "package.json",
                    "content": "{\"dependencies\":{\"next\":\"15\",\"stripe\":\"17\"}}"
                  }
                ]
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/integration/snippets": {
      "get": {
        "summary": "Tested code for a platform and a case",
        "tags": [
          "Agent integration"
        ],
        "operationId": "getIntegrationSnippets",
        "description": "Only code tested against this API, versioned. Cases without a released SDK (modal, native apps) answer not_available_yet.",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "nextjs"
            }
          },
          {
            "name": "case",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "identify",
                "track",
                "webhooks",
                "signed_token",
                "billing_by_api",
                "access_check",
                "headless_cancel",
                "modal",
                "card_update"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the snippets",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration_snippets",
                  "version": "2026-11-06.2",
                  "platform": "rails",
                  "language": "ruby",
                  "api_base_url": "https://api.nochurn.sh",
                  "snippets": [
                    {
                      "case": "webhooks",
                      "status": "available",
                      "title": "Verify NoChurn's webhook signature",
                      "language": "ruby",
                      "label": "Ruby (Net::HTTP, OpenSSL)",
                      "syntax": "ruby",
                      "runs_on": "server",
                      "insert": "A new POST route of your backend (e.g. /webhooks/nochurn), registered with webhook_endpoints_create.",
                      "env": [
                        "NOCHURN_WEBHOOK_SECRET"
                      ],
                      "file": "webhooks/ruby.rb",
                      "code": "# NoChurn: verify a webhook (NoChurn-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"t.<raw body>\">) in Ruby.\n# Env: NOCHURN_WEBHOOK_SECRET (whsec_…). Use the raw body (Rails: request.raw_post).\nrequire \"openssl\"\n\ndef verify_nochurn_signature(raw_body, header, secret = ENV.fetch(\"NOCHURN_WEBHOOK_SECRET\"), tolerance: 300)\n  parts = header.to_s.split(\",\").to_h { |part| part.split(\"=\", 2) }\n  t = Integer(parts[\"t\"].to_s, exception: false)\n  return false if t.nil? || (Time.now.to_i - t).abs > tolerance || parts[\"v1\"].nil?\n\n  expected = OpenSSL::HMAC.hexdigest(\"SHA256\", secret, \"#{parts['t']}.#{raw_body}\")\n  expected.bytesize == parts[\"v1\"].bytesize && OpenSSL.fixed_length_secure_compare(expected, parts[\"v1\"])\nend\n\n# Rails example (skip CSRF on this API controller):\n#   def create\n#     return head(:bad_request) unless verify_nochurn_signature(request.raw_post, request.headers[\"NoChurn-Signature\"])\n#     event = JSON.parse(request.raw_post)\n#     head :ok\n#   end\n"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegrationSnippets"
                }
              }
            }
          },
          "400": {
            "description": "unknown platform",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/subscriptions": {
      "post": {
        "summary": "Send a subscription (billing by API)",
        "tags": [
          "Billing"
        ],
        "operationId": "createSubscription",
        "description": "Idempotent on your external_id: posting it again updates the subscription.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "created with its customer",
            "content": {
              "application/json": {
                "example": {
                  "object": "subscription",
                  "id": "nsub_hd7rffvqSB3fF8LR",
                  "customer": "cst_XBTW4dWsGxyLYcgT",
                  "stripe_subscription_id": null,
                  "external_id": "sub_42",
                  "livemode": false,
                  "status": "active",
                  "plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "interval": "month",
                  "interval_count": 1,
                  "mrr_cents": 4900,
                  "current_period_end": null,
                  "cancel_at_period_end": false,
                  "canceled_at": null,
                  "paused_until": null,
                  "trial_end": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "409": {
            "description": "the account's billing comes from Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "external_id",
                  "customer",
                  "amount",
                  "currency",
                  "interval"
                ],
                "properties": {
                  "external_id": {
                    "type": "string",
                    "example": "sub_42"
                  },
                  "customer": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "external_id": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    ]
                  },
                  "plan": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "integer",
                    "example": 4900
                  },
                  "currency": {
                    "type": "string",
                    "example": "eur"
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "day",
                      "week",
                      "month",
                      "year"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "incomplete",
                      "incomplete_expired",
                      "trialing",
                      "active",
                      "past_due",
                      "canceled",
                      "unpaid",
                      "paused"
                    ]
                  },
                  "current_period_end": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              "example": {
                "external_id": "sub_42",
                "customer": {
                  "external_id": "user_42",
                  "email": "lena@globex.io"
                },
                "plan": "Pro",
                "amount": 4900,
                "currency": "eur",
                "interval": "month"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "List subscriptions, optionally for one customer",
        "tags": [
          "Subscriptions"
        ],
        "operationId": "listSubscriptions",
        "parameters": [
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "cst_…, cus_… or your id."
          },
          {
            "name": "stripe_customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "subscription",
                      "id": "nsub_g7DG7iWyLNMTChNe",
                      "customer": "cst_UJ48q25bPE3jdT3u",
                      "stripe_subscription_id": "sub_1",
                      "external_id": null,
                      "livemode": false,
                      "status": "active",
                      "plan": "Pro",
                      "amount_cents": 4900,
                      "currency": "eur",
                      "interval": "month",
                      "interval_count": 1,
                      "mrr_cents": 4900,
                      "current_period_end": "2026-11-24T10:00:00.000Z",
                      "cancel_at_period_end": false,
                      "canceled_at": null,
                      "paused_until": null,
                      "trial_end": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionList"
                }
              }
            }
          },
          "404": {
            "description": "unknown customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/payments": {
      "post": {
        "summary": "Send a payment (billing by API)",
        "tags": [
          "Billing"
        ],
        "operationId": "createPayment",
        "description": "status failed opens recovery: NoChurn plans retries and requests each one with billing.retry_requested. status succeeded pays the invoice and ends recovery. Idempotent on your external_id.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "a failed payment opens recovery",
            "content": {
              "application/json": {
                "example": {
                  "object": "payment",
                  "id": "npay_TBSmR2L4qghkw8q1",
                  "livemode": false,
                  "customer": "cst_xWuFumaBvkQ9HWYw",
                  "subscription": "nsub_Zv2tbZpFo5YxCaPy",
                  "invoice": "ninv_JKntY71SKsXEe55e",
                  "external_id": "pay_9",
                  "stripe_charge_id": null,
                  "status": "failed",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "decline_code": "do_not_honor",
                  "failure_code": null,
                  "occurred_at": "2026-11-04T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "external_id",
                  "customer",
                  "amount",
                  "currency",
                  "status"
                ],
                "properties": {
                  "external_id": {
                    "type": "string",
                    "example": "pay_9"
                  },
                  "customer": {
                    "type": "string",
                    "example": "user_42"
                  },
                  "subscription": {
                    "type": "string",
                    "example": "sub_42"
                  },
                  "invoice": {
                    "type": "string",
                    "example": "inv_2026_11"
                  },
                  "amount": {
                    "type": "integer",
                    "example": 4900
                  },
                  "currency": {
                    "type": "string",
                    "example": "eur"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "failed",
                      "succeeded"
                    ]
                  },
                  "decline_code": {
                    "type": "string",
                    "example": "insufficient_funds"
                  },
                  "occurred_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "invoice_url": {
                    "type": "string",
                    "description": "Where your customer pays this invoice."
                  }
                }
              },
              "example": {
                "external_id": "pay_9",
                "customer": "user_42",
                "subscription": "sub_42",
                "invoice": "inv_1",
                "amount": 4900,
                "currency": "eur",
                "status": "failed",
                "decline_code": "do_not_honor"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "Payments (Stripe and API)",
        "tags": [
          "Billing"
        ],
        "operationId": "listPayments",
        "parameters": [
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/PaymentList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/subscriptions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "description": "nsub_… or your external_id",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "summary": "Change a subscription sent by API (subscription.cancelled)",
        "tags": [
          "Billing"
        ],
        "operationId": "updateSubscription",
        "parameters": [],
        "responses": {
          "200": {
            "description": "canceled",
            "content": {
              "application/json": {
                "example": {
                  "object": "subscription",
                  "id": "nsub_Wf9KF5qNWWFiJ6N6",
                  "customer": "cst_5SJ8JmubQCqFJzQw",
                  "stripe_subscription_id": null,
                  "external_id": "sub_42",
                  "livemode": false,
                  "status": "canceled",
                  "plan": null,
                  "amount_cents": 4900,
                  "currency": "eur",
                  "interval": "month",
                  "interval_count": 1,
                  "mrr_cents": 0,
                  "current_period_end": null,
                  "cancel_at_period_end": false,
                  "canceled_at": "2026-11-04T10:00:00.000Z",
                  "paused_until": null,
                  "trial_end": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "incomplete",
                      "incomplete_expired",
                      "trialing",
                      "active",
                      "past_due",
                      "canceled",
                      "unpaid",
                      "paused"
                    ]
                  },
                  "plan": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "integer"
                  },
                  "current_period_end": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "cancel_at_period_end": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "status": "canceled"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/api_keys": {
      "get": {
        "summary": "List the API keys of the current mode (live key or NoChurn-Mode header)",
        "tags": [
          "API keys"
        ],
        "operationId": "listApiKeys",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Cursor: the next_cursor of the previous page."
          }
        ],
        "responses": {
          "200": {
            "description": "list of API keys (secret tokens are never included)",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "api_key",
                      "id": "key_KCvgEydsuK5U3G11",
                      "name": "Key 1",
                      "kind": "secret",
                      "mode": "live",
                      "access": "full",
                      "permissions": {
                        "account": "write",
                        "api_keys": "write",
                        "team": "write",
                        "events": "write",
                        "customers": "write",
                        "billing": "write",
                        "stripe_connections": "write",
                        "logs": "write",
                        "flows": "write",
                        "email_domains": "write",
                        "messages": "write",
                        "webhooks": "write",
                        "cancel_sessions": "write",
                        "reviews": "write",
                        "dashboard": "write",
                        "insights": "write",
                        "integrations": "write",
                        "queries": "write",
                        "disputes": "write",
                        "sandbox": "write"
                      },
                      "last4": "G91q",
                      "allowed_origins": null,
                      "token": null,
                      "first_used_at": null,
                      "last_used_at": null,
                      "revoked_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "api_key",
                      "id": "key_5MfRNbFWjVjA9Pwj",
                      "name": "Key 4",
                      "kind": "secret",
                      "mode": "live",
                      "access": "full",
                      "permissions": {
                        "account": "write",
                        "api_keys": "write",
                        "team": "write",
                        "events": "write",
                        "customers": "write",
                        "billing": "write",
                        "stripe_connections": "write",
                        "logs": "write",
                        "flows": "write",
                        "email_domains": "write",
                        "messages": "write",
                        "webhooks": "write",
                        "cancel_sessions": "write",
                        "reviews": "write",
                        "dashboard": "write",
                        "insights": "write",
                        "integrations": "write",
                        "queries": "write",
                        "disputes": "write",
                        "sandbox": "write"
                      },
                      "last4": "hLa2",
                      "allowed_origins": null,
                      "token": null,
                      "first_used_at": "2026-11-04T10:00:00.000Z",
                      "last_used_at": "2026-11-04T10:00:00.000Z",
                      "revoked_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "400": {
            "description": "invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "missing, unknown or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Create an API key (the token is returned once)",
        "tags": [
          "API keys"
        ],
        "operationId": "createApiKey",
        "parameters": [],
        "responses": {
          "201": {
            "description": "API key created, the token is returned once",
            "content": {
              "application/json": {
                "example": {
                  "object": "api_key",
                  "id": "key_GyLvx41FGQQBjCYX",
                  "name": "CI",
                  "kind": "secret",
                  "mode": "test",
                  "access": "full",
                  "permissions": {
                    "account": "write",
                    "api_keys": "write",
                    "team": "write",
                    "events": "write",
                    "customers": "write",
                    "billing": "write",
                    "stripe_connections": "write",
                    "logs": "write",
                    "flows": "write",
                    "email_domains": "write",
                    "messages": "write",
                    "webhooks": "write",
                    "cancel_sessions": "write",
                    "reviews": "write",
                    "dashboard": "write",
                    "insights": "write",
                    "integrations": "write",
                    "queries": "write",
                    "disputes": "write",
                    "sandbox": "write"
                  },
                  "last4": "FLub",
                  "allowed_origins": null,
                  "token": "nc_test_EXAMPLE00000000000000000",
                  "first_used_at": null,
                  "last_used_at": null,
                  "revoked_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                }
              }
            }
          },
          "400": {
            "description": "invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "a test key cannot create a live key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "live",
                      "test"
                    ],
                    "description": "Defaults to the current mode. A test key can only create test keys."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "secret",
                      "publishable"
                    ],
                    "description": "Default: secret. Publishable keys (pk_) are for the cancel widget."
                  },
                  "access": {
                    "type": "string",
                    "enum": [
                      "full",
                      "restricted"
                    ],
                    "description": "Default: full. A key never creates a key more powerful than itself."
                  },
                  "allowed_origins": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "example": "https://app.acme.com"
                    },
                    "description": "Publishable keys only: exact origins allowed to call the public endpoints. Left out, they default to https://<domain> and https://app.<domain> from the email of the user creating the key (else the account owner), unless it is a free or disposable mail domain. A list, even empty, is kept as given."
                  },
                  "permissions": {
                    "type": "object",
                    "additionalProperties": false,
                    "description": "Restricted keys: level per resource (missing = none).",
                    "properties": {
                      "account": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "api_keys": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "team": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "events": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "customers": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "billing": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "stripe_connections": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "logs": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "flows": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "email_domains": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "messages": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "webhooks": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "cancel_sessions": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "reviews": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "dashboard": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "insights": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "integrations": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "queries": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "disputes": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "sandbox": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "name": "CI",
                "mode": "test"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/api_keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Rename a key or change its access",
        "tags": [
          "API keys"
        ],
        "operationId": "updateApiKey",
        "parameters": [],
        "responses": {
          "200": {
            "description": "updated",
            "content": {
              "application/json": {
                "example": {
                  "object": "api_key",
                  "id": "key_mmRMLWbYwsDkVuTW",
                  "name": "CI (read only)",
                  "kind": "secret",
                  "mode": "test",
                  "access": "restricted",
                  "permissions": {
                    "account": "none",
                    "api_keys": "none",
                    "team": "none",
                    "events": "none",
                    "customers": "read",
                    "billing": "none",
                    "stripe_connections": "none",
                    "logs": "none",
                    "flows": "none",
                    "email_domains": "none",
                    "messages": "none",
                    "webhooks": "none",
                    "cancel_sessions": "none",
                    "reviews": "none",
                    "dashboard": "none",
                    "insights": "none",
                    "integrations": "none",
                    "queries": "none",
                    "disputes": "none",
                    "sandbox": "none"
                  },
                  "last4": "zNiY",
                  "allowed_origins": null,
                  "token": null,
                  "first_used_at": null,
                  "last_used_at": null,
                  "revoked_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                }
              }
            }
          },
          "403": {
            "description": "more rights than the calling key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "access": {
                    "type": "string",
                    "enum": [
                      "full",
                      "restricted"
                    ]
                  },
                  "permissions": {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "account": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "api_keys": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "team": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "events": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "customers": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "billing": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "stripe_connections": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "logs": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "flows": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "email_domains": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "messages": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "webhooks": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "cancel_sessions": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "reviews": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "dashboard": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "insights": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "integrations": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "queries": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "disputes": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      },
                      "sandbox": {
                        "type": "string",
                        "enum": [
                          "none",
                          "read",
                          "write"
                        ]
                      }
                    }
                  },
                  "allowed_origins": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "example": "https://app.acme.com"
                    },
                    "description": "Publishable keys only: origins allowed to call the public endpoints."
                  }
                }
              },
              "example": {
                "name": "CI (read only)",
                "access": "restricted",
                "permissions": {
                  "customers": "read"
                }
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Revoke an API key",
        "tags": [
          "API keys"
        ],
        "operationId": "revokeApiKey",
        "responses": {
          "200": {
            "description": "API key revoked",
            "content": {
              "application/json": {
                "example": {
                  "object": "api_key",
                  "id": "key_VYxpFgpYz8anfPXj",
                  "name": "Key 1",
                  "kind": "secret",
                  "mode": "test",
                  "access": "full",
                  "permissions": {
                    "account": "write",
                    "api_keys": "write",
                    "team": "write",
                    "events": "write",
                    "customers": "write",
                    "billing": "write",
                    "stripe_connections": "write",
                    "logs": "write",
                    "flows": "write",
                    "email_domains": "write",
                    "messages": "write",
                    "webhooks": "write",
                    "cancel_sessions": "write",
                    "reviews": "write",
                    "dashboard": "write",
                    "insights": "write",
                    "integrations": "write",
                    "queries": "write",
                    "disputes": "write",
                    "sandbox": "write"
                  },
                  "last4": "oRgd",
                  "allowed_origins": null,
                  "token": null,
                  "first_used_at": null,
                  "last_used_at": null,
                  "revoked_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                }
              }
            }
          },
          "403": {
            "description": "a test key cannot revoke a live key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "key of another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/billing_connections": {
      "get": {
        "summary": "Connected billing providers",
        "tags": [
          "Integrations"
        ],
        "operationId": "listBillingConnections",
        "description": "Every provider with its capability matrix (what NoChurn can do with it). Stripe keeps /v1/stripe_connections.",
        "responses": {
          "200": {
            "description": "with capabilities",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "billing_connection",
                      "id": "scn_LVV5Gkx9djjhXDGN",
                      "provider": "stripe",
                      "livemode": false,
                      "status": "active",
                      "external_account_id": "acct_test1",
                      "business_name": null,
                      "capabilities": {
                        "retries": "nochurn",
                        "card_update": "hosted_page",
                        "offers": [
                          "coupon",
                          "pause",
                          "downgrade"
                        ],
                        "cancel_via_api": true,
                        "disputes": "evidence_api",
                        "fraud_warnings": true,
                        "webhooks": true,
                        "backfill": true
                      },
                      "available": true,
                      "webhook_url": null,
                      "backfill": {
                        "status": "done",
                        "progress": {},
                        "started_at": null,
                        "finished_at": null,
                        "error": null
                      },
                      "webhook_secret_set": true,
                      "settings": {},
                      "internal_id": {
                        "field": null,
                        "defaults": [
                          "metadata.user_id"
                        ],
                        "fields": [
                          "metadata.<name>"
                        ]
                      },
                      "last_event_at": null,
                      "error": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingConnectionList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Connect a provider with API keys",
        "tags": [
          "Integrations"
        ],
        "operationId": "createBillingConnection",
        "description": "Providers arrive milestone by milestone: until then, provider_not_available.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "connected (App Store sandbox): Apple keeps payment, retries and cancellation",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_connection",
                  "id": "bcn_WLPj135TCf4cf9uS",
                  "provider": "chargebee",
                  "livemode": false,
                  "status": "active",
                  "external_account_id": "acme-test",
                  "business_name": "acme-test",
                  "capabilities": {
                    "retries": "provider",
                    "card_update": "provider_portal",
                    "offers": [
                      "coupon",
                      "pause"
                    ],
                    "cancel_via_api": true,
                    "disputes": "provider_handles",
                    "fraud_warnings": false,
                    "webhooks": true,
                    "backfill": true
                  },
                  "available": true,
                  "webhook_url": "https://api.nochurn.sh/webhooks/chargebee/-QEPYRNIzgjDa6SdZxc02GO3oW9oHifu",
                  "backfill": {
                    "status": "pending",
                    "progress": {},
                    "started_at": null,
                    "finished_at": null,
                    "error": null
                  },
                  "webhook_secret_set": true,
                  "settings": {},
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id",
                      "cf_user_id"
                    ],
                    "fields": [
                      "metadata.<name>",
                      "cf_<name>",
                      "id"
                    ]
                  },
                  "last_event_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "webhook_setup": {
                    "basic_auth_username": "nochurn",
                    "basic_auth_password": "EXAMPLE00000000000000000",
                    "api_version": "v2"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingConnection"
                }
              }
            }
          },
          "400": {
            "description": "unknown provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider"
                ],
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "chargebee",
                      "recurly",
                      "maxio",
                      "zuora",
                      "paddle",
                      "lemon_squeezy",
                      "polar",
                      "braintree",
                      "mollie",
                      "gocardless",
                      "razorpay",
                      "app_store",
                      "google_play",
                      "custom"
                    ]
                  },
                  "credentials": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "The fields listed by GET /v1/billing_providers (e.g. site, api_key, region, webhook_secret; App Store: private_key, key_id, issuer_id, bundle_id; Google Play: service_account_json, package_name)."
                  },
                  "settings": {
                    "type": "object",
                    "description": "Stores only: refund_review { customer_consented, sample_content_provided } (booleans); Google Play: push_audience, push_service_account_email (\"\" clears).",
                    "properties": {
                      "refund_review": {
                        "type": "object",
                        "properties": {
                          "customer_consented": {
                            "type": "boolean"
                          },
                          "sample_content_provided": {
                            "type": "boolean"
                          }
                        }
                      },
                      "push_audience": {
                        "type": "string"
                      },
                      "push_service_account_email": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "provider": "chargebee",
                "credentials": {
                  "site": "acme-test",
                  "api_key": "test_x"
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/billing_connections/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "get": {
        "summary": "A billing connection",
        "tags": [
          "Integrations"
        ],
        "operationId": "getBillingConnection",
        "responses": {
          "200": {
            "description": "the connection",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_connection",
                  "id": "scn_4XXipZYv1tnXU99X",
                  "provider": "stripe",
                  "livemode": false,
                  "status": "active",
                  "external_account_id": "acct_test1",
                  "business_name": null,
                  "capabilities": {
                    "retries": "nochurn",
                    "card_update": "hosted_page",
                    "offers": [
                      "coupon",
                      "pause",
                      "downgrade"
                    ],
                    "cancel_via_api": true,
                    "disputes": "evidence_api",
                    "fraud_warnings": true,
                    "webhooks": true,
                    "backfill": true
                  },
                  "available": true,
                  "webhook_url": null,
                  "backfill": {
                    "status": "done",
                    "progress": {},
                    "started_at": null,
                    "finished_at": null,
                    "error": null
                  },
                  "webhook_secret_set": true,
                  "settings": {},
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id"
                    ],
                    "fields": [
                      "metadata.<name>"
                    ]
                  },
                  "last_event_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingConnection"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Change capabilities, the Stripe retry mode or where your user id is read",
        "tags": [
          "Integrations"
        ],
        "operationId": "updateBillingConnection",
        "parameters": [],
        "responses": {
          "200": {
            "description": "updated",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_connection",
                  "id": "scn_qHHubuDxvBUkk4ZN",
                  "provider": "stripe",
                  "livemode": false,
                  "status": "active",
                  "external_account_id": "acct_test1",
                  "business_name": null,
                  "capabilities": {
                    "retries": "provider",
                    "card_update": "hosted_page",
                    "offers": [
                      "coupon",
                      "pause",
                      "downgrade"
                    ],
                    "cancel_via_api": true,
                    "disputes": "evidence_api",
                    "fraud_warnings": true,
                    "webhooks": true,
                    "backfill": true
                  },
                  "available": true,
                  "webhook_url": null,
                  "backfill": {
                    "status": "done",
                    "progress": {},
                    "started_at": null,
                    "finished_at": null,
                    "error": null
                  },
                  "webhook_secret_set": true,
                  "settings": {},
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id"
                    ],
                    "fields": [
                      "metadata.<name>"
                    ]
                  },
                  "last_event_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingConnection"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "capabilities": {
                    "type": "object",
                    "description": "Only the keys sent change; locked ones are refused (GET /v1/billing_providers).",
                    "properties": {
                      "retries": {
                        "type": "string",
                        "enum": [
                          "nochurn",
                          "provider",
                          "none"
                        ]
                      },
                      "card_update": {
                        "type": "string",
                        "enum": [
                          "hosted_page",
                          "provider_portal",
                          "none"
                        ]
                      },
                      "offers": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "enum": [
                            "coupon",
                            "pause",
                            "downgrade"
                          ]
                        }
                      },
                      "cancel_via_api": {
                        "type": "boolean"
                      }
                    }
                  },
                  "smart_retries_mode": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "nochurn"
                    ]
                  },
                  "internal_id_field": {
                    "type": "string",
                    "nullable": true,
                    "example": "custom_fields.user_id",
                    "description": "Owner / admin. Where this tool keeps your app's id for a customer (a field or one key inside one, see internal_id.fields); null or \"\" = the tool's defaults. Applies to what NoChurn reads from now on."
                  },
                  "webhook_secret": {
                    "type": "string",
                    "description": "Write only: the secret of the webhook endpoint created on webhook_url (Recurly, Paddle, GoCardless, Razorpay)."
                  },
                  "settings": {
                    "type": "object",
                    "description": "Stores only: refund_review { customer_consented, sample_content_provided } (booleans); Google Play: push_audience, push_service_account_email (\"\" clears).",
                    "properties": {
                      "refund_review": {
                        "type": "object",
                        "properties": {
                          "customer_consented": {
                            "type": "boolean"
                          },
                          "sample_content_provided": {
                            "type": "boolean"
                          }
                        }
                      },
                      "push_audience": {
                        "type": "string"
                      },
                      "push_service_account_email": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "smart_retries_mode": "stripe"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Disconnect (credentials erased; synced data stays)",
        "tags": [
          "Integrations"
        ],
        "operationId": "deleteBillingConnection",
        "responses": {
          "200": {
            "description": "revoked",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_connection",
                  "id": "bcn_G5sqaQTvcqqq3YGa",
                  "provider": "chargebee",
                  "livemode": false,
                  "status": "revoked",
                  "external_account_id": "acme-test",
                  "business_name": null,
                  "capabilities": {
                    "retries": "provider",
                    "card_update": "provider_portal",
                    "offers": [
                      "coupon",
                      "pause"
                    ],
                    "cancel_via_api": true,
                    "disputes": "provider_handles",
                    "fraud_warnings": false,
                    "webhooks": true,
                    "backfill": true
                  },
                  "available": true,
                  "webhook_url": null,
                  "backfill": {
                    "status": "done",
                    "progress": {},
                    "started_at": null,
                    "finished_at": null,
                    "error": null
                  },
                  "webhook_secret_set": false,
                  "settings": {},
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id",
                      "cf_user_id"
                    ],
                    "fields": [
                      "metadata.<name>",
                      "cf_<name>",
                      "id"
                    ]
                  },
                  "last_event_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingConnection"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/billing_connections/{id}/backfill": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Import again (12 months)",
        "tags": [
          "Integrations"
        ],
        "operationId": "backfillBillingConnection",
        "responses": {
          "202": {
            "description": "queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_connection",
                  "id": "bcn_bd2nxUWHSGoShYqJ",
                  "provider": "chargebee",
                  "livemode": false,
                  "status": "active",
                  "external_account_id": "acme-test",
                  "business_name": null,
                  "capabilities": {
                    "retries": "provider",
                    "card_update": "provider_portal",
                    "offers": [
                      "coupon",
                      "pause"
                    ],
                    "cancel_via_api": true,
                    "disputes": "provider_handles",
                    "fraud_warnings": false,
                    "webhooks": true,
                    "backfill": true
                  },
                  "available": true,
                  "webhook_url": "https://api.nochurn.sh/webhooks/chargebee/tok_cb2",
                  "backfill": {
                    "status": "pending",
                    "progress": {},
                    "started_at": null,
                    "finished_at": null,
                    "error": null
                  },
                  "webhook_secret_set": false,
                  "settings": {},
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id",
                      "cf_user_id"
                    ],
                    "fields": [
                      "metadata.<name>",
                      "cf_<name>",
                      "id"
                    ]
                  },
                  "last_event_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingConnection"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/billing_providers": {
      "get": {
        "summary": "Billing providers (supported and coming)",
        "tags": [
          "Integrations"
        ],
        "operationId": "listBillingProviders",
        "responses": {
          "200": {
            "description": "every provider, available or not",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "billing_provider",
                      "provider": "stripe",
                      "family": "billing",
                      "available": true,
                      "connect": "oauth",
                      "credentials": [],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "hosted_page",
                        "offers": [
                          "coupon",
                          "pause",
                          "downgrade"
                        ],
                        "cancel_via_api": true,
                        "disputes": "evidence_api",
                        "fraud_warnings": true,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [],
                      "webhook_secret": "none",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/stripe/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "chargebee",
                      "family": "billing",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "site",
                          "secret": false,
                          "hint": "The part before .chargebee.com (test site: <name>-test)"
                        },
                        {
                          "name": "api_key",
                          "secret": true,
                          "hint": "Settings › Configure Chargebee › API keys (full access)"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "coupon",
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [],
                      "webhook_secret": "generated",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/chargebee/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "recurly",
                      "family": "billing",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "api_key",
                          "secret": true,
                          "hint": "Integrations › API credentials (private key)"
                        },
                        {
                          "name": "region",
                          "secret": false,
                          "hint": "us or eu",
                          "options": [
                            "us",
                            "eu"
                          ]
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "coupon",
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [],
                      "webhook_secret": "pasted",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/recurly/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "maxio",
                      "family": "billing",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "subdomain",
                          "secret": false,
                          "hint": "The part before .chargify.com, or the full address of an EU site (acme.ebilling.maxio.com). A site in test mode in Test mode"
                        },
                        {
                          "name": "api_key",
                          "secret": true,
                          "hint": "Config › Integrations › API keys"
                        },
                        {
                          "name": "site_shared_key",
                          "secret": true,
                          "hint": "Config › Settings › Site shared key: signs the webhooks"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "coupon"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [],
                      "webhook_secret": "none",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/maxio/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "zuora",
                      "family": "billing",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "environment",
                          "secret": false,
                          "hint": "Where your tenant lives (sandbox or test in Test mode)",
                          "options": [
                            "sandbox",
                            "test",
                            "na_sandbox",
                            "eu_sandbox",
                            "eu_test",
                            "ap_test",
                            "production",
                            "na_production",
                            "eu_production",
                            "ap_production"
                          ]
                        },
                        {
                          "name": "client_id",
                          "secret": false,
                          "hint": "Settings › Administration › Manage users › OAuth client (API user with read and subscription rights)"
                        },
                        {
                          "name": "client_secret",
                          "secret": true,
                          "hint": "Shown once when the OAuth client is created"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "none",
                        "offers": [
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": false,
                        "backfill": true
                      },
                      "locked": [
                        "card_update"
                      ],
                      "webhook_secret": "none",
                      "hint": "NoChurn reads your Zuora tenant every 10 minutes (accounts, subscriptions, invoices, payments): no callout to configure. Retries and the payment page stay with Zuora.",
                      "docs_url": "https://nochurn.sh/docs/integrations/zuora/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "paddle",
                      "family": "merchant_of_record",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "api_key",
                          "secret": true,
                          "hint": "Developer tools › Authentication (sandbox key in Test mode), with the Customer portal session permission (card update links)"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "coupon",
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "retries",
                        "disputes"
                      ],
                      "webhook_secret": "pasted",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/paddle/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "lemon_squeezy",
                      "family": "merchant_of_record",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "api_key",
                          "secret": true,
                          "hint": "Settings › API (a test mode key in Test mode)"
                        },
                        {
                          "name": "store_id",
                          "secret": false,
                          "hint": "Settings › Stores: the store's number"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "retries",
                        "disputes"
                      ],
                      "webhook_secret": "pasted",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/lemon-squeezy/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "polar",
                      "family": "merchant_of_record",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "access_token",
                          "secret": true,
                          "hint": "Settings › Developers › Organization access token (a sandbox.polar.sh token in Test mode)"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "coupon",
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "provider_handles",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "retries",
                        "disputes"
                      ],
                      "webhook_secret": "pasted",
                      "hint": "Create a webhook endpoint in Polar › Settings › Webhooks with webhook_url, format Raw and API version 2026-10 (the version NoChurn reads), then paste its secret (whsec_…) here.",
                      "docs_url": "https://nochurn.sh/docs/integrations/polar/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "braintree",
                      "family": "payments",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "merchant_id",
                          "secret": false,
                          "hint": "Settings › Business (sandbox account in Test mode)"
                        },
                        {
                          "name": "public_key",
                          "secret": false,
                          "hint": "Settings › API › API keys"
                        },
                        {
                          "name": "private_key",
                          "secret": true,
                          "hint": "Settings › API › API keys (View)"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "none",
                        "offers": [
                          "coupon"
                        ],
                        "cancel_via_api": true,
                        "disputes": "evidence_api",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "card_update"
                      ],
                      "webhook_secret": "none",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/braintree/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "mollie",
                      "family": "payments",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "api_key",
                          "secret": true,
                          "hint": "Developers › API keys (test_… in Test mode, live_… in Live mode)"
                        }
                      ],
                      "capabilities": {
                        "retries": "nochurn",
                        "card_update": "hosted_page",
                        "offers": [],
                        "cancel_via_api": true,
                        "disputes": "none",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "disputes"
                      ],
                      "webhook_secret": "none",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/mollie/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "gocardless",
                      "family": "payments",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "access_token",
                          "secret": true,
                          "hint": "Developers › Create › Access token, read-write (sandbox token in Test mode)"
                        }
                      ],
                      "capabilities": {
                        "retries": "nochurn",
                        "card_update": "hosted_page",
                        "offers": [
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "none",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "disputes"
                      ],
                      "webhook_secret": "pasted",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/gocardless/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "razorpay",
                      "family": "payments",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "key_id",
                          "secret": false,
                          "hint": "Account & Settings › API keys (rzp_test_… in Test mode)"
                        },
                        {
                          "name": "key_secret",
                          "secret": true,
                          "hint": "Shown once when the key is generated"
                        }
                      ],
                      "capabilities": {
                        "retries": "provider",
                        "card_update": "provider_portal",
                        "offers": [
                          "pause"
                        ],
                        "cancel_via_api": true,
                        "disputes": "evidence_api",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "retries",
                        "offers"
                      ],
                      "webhook_secret": "pasted",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/razorpay/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "app_store",
                      "family": "stores",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "private_key",
                          "secret": true,
                          "hint": "The .p8 file of an In-App Purchase key (App Store Connect › Users and Access › Integrations › In-App Purchase), pasted whole"
                        },
                        {
                          "name": "key_id",
                          "secret": false,
                          "hint": "The key's ID, next to it in App Store Connect"
                        },
                        {
                          "name": "issuer_id",
                          "secret": false,
                          "hint": "Issuer ID, at the top of the same page"
                        },
                        {
                          "name": "bundle_id",
                          "secret": false,
                          "hint": "Your app's bundle ID (e.g. com.acme.app)"
                        }
                      ],
                      "capabilities": {
                        "retries": "none",
                        "card_update": "none",
                        "offers": [],
                        "cancel_via_api": false,
                        "disputes": "refund_review",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "retries",
                        "card_update",
                        "disputes"
                      ],
                      "webhook_secret": "none",
                      "hint": "Apple shares no email: customers are linked only when your app sets an appAccountToken at purchase and sends it to NoChurn (as the external_id, or the app_account_token attribute of the customer). Without it, events stay unlinked. Paste webhook_url as the App Store Server Notifications V2 URL (sandbox URL in Test mode).",
                      "docs_url": "https://nochurn.sh/docs/integrations/app-store/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "google_play",
                      "family": "stores",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "service_account_json",
                          "secret": true,
                          "hint": "JSON key of a Google Cloud service account invited in Play Console › Users and permissions (view financial data, manage orders and subscriptions)"
                        },
                        {
                          "name": "package_name",
                          "secret": false,
                          "hint": "Your app's package name (e.g. com.acme.app)"
                        }
                      ],
                      "capabilities": {
                        "retries": "none",
                        "card_update": "none",
                        "offers": [],
                        "cancel_via_api": true,
                        "disputes": "refund_review",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": true
                      },
                      "locked": [
                        "retries",
                        "card_update",
                        "disputes"
                      ],
                      "webhook_secret": "none",
                      "hint": "Customers are linked only when your app sets obfuscatedAccountId at purchase and sends it to NoChurn (as the external_id, or the obfuscated_account_id attribute of the customer). Without it, events stay unlinked. Create a Pub/Sub push subscription to webhook_url on the topic of your real-time developer notifications. Test mode applies license testers' purchases only.",
                      "docs_url": "https://nochurn.sh/docs/integrations/google-play/"
                    },
                    {
                      "object": "billing_provider",
                      "provider": "custom",
                      "family": "custom",
                      "available": true,
                      "connect": "credentials",
                      "credentials": [
                        {
                          "name": "name",
                          "secret": false,
                          "hint": "The name of your billing system, as shown in NoChurn"
                        },
                        {
                          "name": "signature_mode",
                          "secret": false,
                          "hint": "How your billing system signs its webhooks",
                          "options": [
                            "hmac_sha256",
                            "header_secret"
                          ]
                        },
                        {
                          "name": "signature_header",
                          "secret": false,
                          "hint": "The header carrying the signature or the secret (e.g. X-Signature)"
                        },
                        {
                          "name": "signature_encoding",
                          "secret": false,
                          "hint": "HMAC only: how the signature is written",
                          "options": [
                            "hex",
                            "base64"
                          ]
                        }
                      ],
                      "capabilities": {
                        "retries": "none",
                        "card_update": "none",
                        "offers": [],
                        "cancel_via_api": false,
                        "disputes": "none",
                        "fraud_warnings": false,
                        "webhooks": true,
                        "backfill": false
                      },
                      "locked": [
                        "retries",
                        "card_update",
                        "offers",
                        "cancel_via_api",
                        "disputes",
                        "fraud_warnings",
                        "webhooks",
                        "backfill"
                      ],
                      "webhook_secret": "pasted",
                      "hint": null,
                      "docs_url": "https://nochurn.sh/docs/integrations/custom/"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingProviderList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/billing_connections/{billing_connection_id}/mapping": {
      "parameters": [
        {
          "name": "billing_connection_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Mapping of a custom billing connection, tested on its sample",
        "tags": [
          "Integrations"
        ],
        "operationId": "getBillingMapping",
        "responses": {
          "200": {
            "description": "rules, state and the last webhooks with their result",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_mapping",
                  "billing_connection": "bcn_pEFhqdfNzf8kNZu4",
                  "active": false,
                  "activated_at": null,
                  "rules": [],
                  "types": [
                    "customer.upserted",
                    "subscription.created",
                    "subscription.updated",
                    "subscription.paused",
                    "subscription.resumed",
                    "subscription.canceled",
                    "subscription.renewed",
                    "invoice.paid",
                    "invoice.payment_failed",
                    "invoice.uncollectible",
                    "invoice.updated"
                  ],
                  "fields": {
                    "customer": [
                      "id",
                      "email",
                      "name",
                      "country",
                      "locale",
                      "external_id"
                    ],
                    "subscription": [
                      "id",
                      "status",
                      "plan",
                      "amount_cents",
                      "currency",
                      "interval",
                      "interval_count",
                      "current_period_end",
                      "cancel_at_period_end",
                      "started_at",
                      "canceled_at",
                      "ended_at"
                    ],
                    "invoice": [
                      "id",
                      "subscription_id",
                      "status",
                      "amount_due_cents",
                      "amount_paid_cents",
                      "amount_remaining_cents",
                      "currency",
                      "attempt_count",
                      "decline_code",
                      "failure_code",
                      "next_attempt_at",
                      "hosted_invoice_url"
                    ]
                  },
                  "transforms": [
                    "units_to_cents",
                    "unix_seconds",
                    "unix_millis",
                    "lowercase",
                    "boolean",
                    "string"
                  ],
                  "sample": [
                    {
                      "id": "bws_UdGw7Rgf3vMuJkXV",
                      "received_at": "2026-11-04T10:00:00Z",
                      "payload": {
                        "id": "wh_1",
                        "data": {
                          "email": "ana@example.test",
                          "amount": "49.90",
                          "invoice": "inv_wh_1",
                          "currency": "eur",
                          "customer": "c_1"
                        },
                        "type": "invoice.failed"
                      },
                      "result": {
                        "status": "ignored",
                        "rule": null,
                        "type": null,
                        "event_id": null,
                        "data": null,
                        "errors": []
                      }
                    }
                  ],
                  "summary": {
                    "total": 1,
                    "events": 0,
                    "errors": 0,
                    "ignored": 1
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingMapping"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "put": {
        "summary": "Save the mapping rules (tested on the sample)",
        "tags": [
          "Integrations"
        ],
        "operationId": "updateBillingMapping",
        "parameters": [],
        "responses": {
          "200": {
            "description": "saved; each sample webhook with what it becomes",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_mapping",
                  "billing_connection": "bcn_mVxfYN2KgqTevEbE",
                  "active": false,
                  "activated_at": null,
                  "rules": [
                    {
                      "when": {
                        "path": "$.type",
                        "equals": "invoice.failed"
                      },
                      "type": "invoice.payment_failed",
                      "event_id": "$.id",
                      "fields": {
                        "customer.id": "$.data.customer",
                        "customer.email": "$.data.email",
                        "invoice.id": "$.data.invoice",
                        "invoice.amount_due_cents": {
                          "path": "$.data.amount",
                          "transform": "units_to_cents"
                        },
                        "invoice.currency": "$.data.currency"
                      }
                    }
                  ],
                  "types": [
                    "customer.upserted",
                    "subscription.created",
                    "subscription.updated",
                    "subscription.paused",
                    "subscription.resumed",
                    "subscription.canceled",
                    "subscription.renewed",
                    "invoice.paid",
                    "invoice.payment_failed",
                    "invoice.uncollectible",
                    "invoice.updated"
                  ],
                  "fields": {
                    "customer": [
                      "id",
                      "email",
                      "name",
                      "country",
                      "locale",
                      "external_id"
                    ],
                    "subscription": [
                      "id",
                      "status",
                      "plan",
                      "amount_cents",
                      "currency",
                      "interval",
                      "interval_count",
                      "current_period_end",
                      "cancel_at_period_end",
                      "started_at",
                      "canceled_at",
                      "ended_at"
                    ],
                    "invoice": [
                      "id",
                      "subscription_id",
                      "status",
                      "amount_due_cents",
                      "amount_paid_cents",
                      "amount_remaining_cents",
                      "currency",
                      "attempt_count",
                      "decline_code",
                      "failure_code",
                      "next_attempt_at",
                      "hosted_invoice_url"
                    ]
                  },
                  "transforms": [
                    "units_to_cents",
                    "unix_seconds",
                    "unix_millis",
                    "lowercase",
                    "boolean",
                    "string"
                  ],
                  "sample": [
                    {
                      "id": "bws_rcEbEUxNJYg4jyS4",
                      "received_at": "2026-11-04T10:00:00Z",
                      "payload": {
                        "id": "wh_1",
                        "data": {
                          "email": "ana@example.test",
                          "amount": "49.90",
                          "invoice": "inv_wh_1",
                          "currency": "eur",
                          "customer": "c_1"
                        },
                        "type": "invoice.failed"
                      },
                      "result": {
                        "status": "event",
                        "rule": 0,
                        "type": "invoice.payment_failed",
                        "event_id": "wh_1",
                        "data": {
                          "invoice": {
                            "currency": "eur",
                            "id": "inv_wh_1",
                            "amount_due_cents": 4990
                          },
                          "customer": {
                            "id": "c_1",
                            "email": "ana@example.test"
                          }
                        },
                        "errors": []
                      }
                    }
                  ],
                  "summary": {
                    "total": 1,
                    "events": 1,
                    "errors": 0,
                    "ignored": 0
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingMapping"
                }
              }
            }
          },
          "400": {
            "description": "invalid rules",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rules"
                ],
                "properties": {
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              },
              "example": {
                "rules": [
                  {
                    "when": {
                      "path": "$.type",
                      "equals": "invoice.failed"
                    },
                    "type": "invoice.payment_failed",
                    "event_id": "$.id",
                    "fields": {
                      "customer.id": "$.data.customer",
                      "customer.email": "$.data.email",
                      "invoice.id": "$.data.invoice",
                      "invoice.amount_due_cents": {
                        "path": "$.data.amount",
                        "transform": "units_to_cents"
                      },
                      "invoice.currency": "$.data.currency"
                    }
                  }
                ]
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/billing_connections/{billing_connection_id}/mapping/activate": {
      "parameters": [
        {
          "name": "billing_connection_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Turn the mapping on (the sample must pass)",
        "tags": [
          "Integrations"
        ],
        "operationId": "activateBillingMapping",
        "responses": {
          "200": {
            "description": "on",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_mapping",
                  "billing_connection": "bcn_7ihcvkgpzms1J5ee",
                  "active": true,
                  "activated_at": "2026-11-04T10:00:00Z",
                  "rules": [
                    {
                      "type": "invoice.payment_failed",
                      "when": {
                        "path": "$.type",
                        "equals": "invoice.failed"
                      },
                      "fields": {
                        "invoice.id": "$.data.invoice",
                        "customer.id": "$.data.customer",
                        "customer.email": "$.data.email",
                        "invoice.currency": "$.data.currency",
                        "invoice.amount_due_cents": {
                          "path": "$.data.amount",
                          "transform": "units_to_cents"
                        }
                      },
                      "event_id": "$.id"
                    }
                  ],
                  "types": [
                    "customer.upserted",
                    "subscription.created",
                    "subscription.updated",
                    "subscription.paused",
                    "subscription.resumed",
                    "subscription.canceled",
                    "subscription.renewed",
                    "invoice.paid",
                    "invoice.payment_failed",
                    "invoice.uncollectible",
                    "invoice.updated"
                  ],
                  "fields": {
                    "customer": [
                      "id",
                      "email",
                      "name",
                      "country",
                      "locale",
                      "external_id"
                    ],
                    "subscription": [
                      "id",
                      "status",
                      "plan",
                      "amount_cents",
                      "currency",
                      "interval",
                      "interval_count",
                      "current_period_end",
                      "cancel_at_period_end",
                      "started_at",
                      "canceled_at",
                      "ended_at"
                    ],
                    "invoice": [
                      "id",
                      "subscription_id",
                      "status",
                      "amount_due_cents",
                      "amount_paid_cents",
                      "amount_remaining_cents",
                      "currency",
                      "attempt_count",
                      "decline_code",
                      "failure_code",
                      "next_attempt_at",
                      "hosted_invoice_url"
                    ]
                  },
                  "transforms": [
                    "units_to_cents",
                    "unix_seconds",
                    "unix_millis",
                    "lowercase",
                    "boolean",
                    "string"
                  ],
                  "sample": [
                    {
                      "id": "bws_VCjNWusA1bhGbbGR",
                      "received_at": "2026-11-04T10:00:00Z",
                      "payload": {
                        "id": "wh_1",
                        "data": {
                          "email": "ana@example.test",
                          "amount": "49.90",
                          "invoice": "inv_wh_1",
                          "currency": "eur",
                          "customer": "c_1"
                        },
                        "type": "invoice.failed"
                      },
                      "result": {
                        "status": "event",
                        "rule": 0,
                        "type": "invoice.payment_failed",
                        "event_id": "wh_1",
                        "data": {
                          "invoice": {
                            "currency": "eur",
                            "id": "inv_wh_1",
                            "amount_due_cents": 4990
                          },
                          "customer": {
                            "id": "c_1",
                            "email": "ana@example.test"
                          }
                        },
                        "errors": []
                      }
                    }
                  ],
                  "summary": {
                    "total": 1,
                    "events": 1,
                    "errors": 0,
                    "ignored": 0
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingMapping"
                }
              }
            }
          },
          "409": {
            "description": "the sample does not pass yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/billing_connections/{billing_connection_id}/mapping/deactivate": {
      "parameters": [
        {
          "name": "billing_connection_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Turn the mapping off (webhooks are only kept as a sample)",
        "tags": [
          "Integrations"
        ],
        "operationId": "deactivateBillingMapping",
        "responses": {
          "200": {
            "description": "off",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_mapping",
                  "billing_connection": "bcn_omV4hFm5EZYzNGTC",
                  "active": false,
                  "activated_at": null,
                  "rules": [],
                  "types": [
                    "customer.upserted",
                    "subscription.created",
                    "subscription.updated",
                    "subscription.paused",
                    "subscription.resumed",
                    "subscription.canceled",
                    "subscription.renewed",
                    "invoice.paid",
                    "invoice.payment_failed",
                    "invoice.uncollectible",
                    "invoice.updated"
                  ],
                  "fields": {
                    "customer": [
                      "id",
                      "email",
                      "name",
                      "country",
                      "locale",
                      "external_id"
                    ],
                    "subscription": [
                      "id",
                      "status",
                      "plan",
                      "amount_cents",
                      "currency",
                      "interval",
                      "interval_count",
                      "current_period_end",
                      "cancel_at_period_end",
                      "started_at",
                      "canceled_at",
                      "ended_at"
                    ],
                    "invoice": [
                      "id",
                      "subscription_id",
                      "status",
                      "amount_due_cents",
                      "amount_paid_cents",
                      "amount_remaining_cents",
                      "currency",
                      "attempt_count",
                      "decline_code",
                      "failure_code",
                      "next_attempt_at",
                      "hosted_invoice_url"
                    ]
                  },
                  "transforms": [
                    "units_to_cents",
                    "unix_seconds",
                    "unix_millis",
                    "lowercase",
                    "boolean",
                    "string"
                  ],
                  "sample": [],
                  "summary": {
                    "total": 0,
                    "events": 0,
                    "errors": 0,
                    "ignored": 0
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingMapping"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/invoices": {
      "get": {
        "summary": "List invoices, optionally for one customer",
        "tags": [
          "Invoices"
        ],
        "operationId": "listInvoices",
        "parameters": [
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "cst_…, cus_… or your id."
          },
          {
            "name": "stripe_customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "invoice",
                      "id": "ninv_WL2dwPKmWFvGVwFH",
                      "customer": "cst_LcPL9ouMxcj8urYd",
                      "subscription": "nsub_GvBC2Nd8WchY2sb4",
                      "stripe_invoice_id": "in_1",
                      "external_id": null,
                      "livemode": false,
                      "number": "INV-1",
                      "status": "open",
                      "amount_due_cents": 4900,
                      "amount_paid_cents": 0,
                      "amount_remaining_cents": 4900,
                      "currency": "eur",
                      "attempt_count": 1,
                      "next_attempt_at": "2026-11-07T10:00:00.000Z",
                      "failure_code": null,
                      "decline_code": null,
                      "hosted_invoice_url": "https://invoice.stripe.com/i/test",
                      "paid_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/InvoiceList"
                }
              }
            }
          },
          "404": {
            "description": "unknown customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/payment_methods": {
      "get": {
        "summary": "List payment methods, optionally for one customer",
        "tags": [
          "Payment methods"
        ],
        "operationId": "listPaymentMethods",
        "parameters": [
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "cst_…, cus_… or your id."
          },
          {
            "name": "stripe_customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "payment_method",
                      "id": "npm_ToLxz1qiAXPyXm2j",
                      "customer": "cst_TDtZmEc1VKdDucfv",
                      "stripe_payment_method_id": "pm_1",
                      "livemode": false,
                      "type": "card",
                      "brand": "visa",
                      "last4": "4242",
                      "exp_month": 12,
                      "exp_year": 2030,
                      "funding": "credit",
                      "country": "FR",
                      "wallet": null,
                      "is_default": false,
                      "detached_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/PaymentMethodList"
                }
              }
            }
          },
          "404": {
            "description": "unknown customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/logos": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "cancel, or the cancel flow's flw_…"
        }
      ],
      "post": {
        "summary": "Upload a logo for the cancel modal (admin+)",
        "tags": [
          "Flows"
        ],
        "operationId": "uploadCancelFlowLogo",
        "description": "PNG, JPEG or WebP, 1 MB and 4000 px per side at most; the type is read from the file's bytes, not its name (SVG is refused: it can carry script). Stored by NoChurn (S3 in production). Returns the logo and its public url, served without auth at an unguessable path: put it in draft.design.logo_url (PATCH /v1/flows/cancel) and save the draft — the upload alone does not change the draft. Resource flows (write).",
        "parameters": [],
        "responses": {
          "201": {
            "description": "stored, with cancel_flow.logo_uploaded",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_flow_logo",
                  "id": "clg_a64GDD84khnhiTj2",
                  "flow": "flw_xYvYtMQxrYYHDSbn",
                  "url": "https://api.nochurn.sh/assets/logos/2yfX8g5SnJZbznKJCmpExdYFiSEyrQ6t.png",
                  "content_type": "image/png",
                  "byte_size": 45,
                  "width": 240,
                  "height": 80,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelFlowLogo"
                }
              }
            }
          },
          "400": {
            "description": "another flow than the cancel flow",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "over 1 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "not a PNG, JPEG or WebP (read from the bytes): an SVG named logo.png",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PNG, JPEG or WebP, at most 1 MB."
                  }
                }
              },
              "example": {
                "file": "logo.png"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/flows/{id}/logos/{logo_id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "logo_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "clg_…"
        }
      ],
      "delete": {
        "summary": "Remove a logo (admin+)",
        "tags": [
          "Flows"
        ],
        "operationId": "removeCancelFlowLogo",
        "description": "Takes it out of the draft at once (design.logo_url becomes null when it was this logo). The file is deleted once no published version, segment or A/B variant uses it, so a running modal never loses its logo.",
        "responses": {
          "200": {
            "description": "removed, with cancel_flow.logo_removed; the flow with its draft",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_bv4fLvDSqHLBcmKP",
                  "kind": "cancel",
                  "key": "default",
                  "name": "Cancel flow",
                  "status": "draft",
                  "holdout_percent": 5,
                  "test_version": null,
                  "live_version": null,
                  "unpublished_changes": [
                    "design"
                  ],
                  "mode_status": {
                    "livemode": false,
                    "state": "draft",
                    "version": null,
                    "published_at": null,
                    "unpublished_changes": [
                      "design"
                    ]
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "pause": {
                      "inapp": "none"
                    },
                    "steps": [
                      "reason",
                      "offer",
                      "feedback",
                      "confirmation"
                    ],
                    "texts": {},
                    "design": {
                      "preset": "light",
                      "logo_url": null,
                      "css_report": {
                        "errors": [],
                        "cancel_button_check": "ok"
                      },
                      "custom_css": ""
                    },
                    "reasons": [
                      {
                        "key": "too_expensive",
                        "labels": {
                          "de": "Es ist zu teuer",
                          "en": "It's too expensive",
                          "fr": "C'est trop cher",
                          "it": "È troppo caro",
                          "pt": "É demasiado caro"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "missing_feature",
                        "labels": {
                          "de": "Eine Funktion fehlt",
                          "en": "A feature is missing",
                          "fr": "Il manque une fonctionnalité",
                          "it": "Manca una funzionalità",
                          "pt": "Falta uma funcionalidade"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "not_using",
                        "labels": {
                          "de": "Ich nutze es zu wenig",
                          "en": "I don't use it enough",
                          "fr": "Je ne l'utilise pas assez",
                          "it": "Non lo uso abbastanza",
                          "pt": "Não o uso o suficiente"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "switching",
                        "labels": {
                          "de": "Ich wechsle zu einem anderen Tool",
                          "en": "I'm switching to another tool",
                          "fr": "Je passe à un autre outil",
                          "it": "Passo a un altro strumento",
                          "pt": "Vou mudar para outra ferramenta"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "temporary",
                        "labels": {
                          "de": "Ich brauche nur eine Pause",
                          "en": "I only need a break",
                          "fr": "J'ai juste besoin d'une pause",
                          "it": "Ho solo bisogno di una pausa",
                          "pt": "Só preciso de uma pausa"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "project_finished",
                        "labels": {
                          "de": "Mein Projekt ist abgeschlossen",
                          "en": "My project is finished",
                          "fr": "Mon projet est terminé",
                          "it": "Il mio progetto è finito",
                          "pt": "O meu projeto terminou"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "other",
                        "labels": {
                          "de": "Etwas anderes",
                          "en": "Something else",
                          "fr": "Autre chose",
                          "it": "Altro",
                          "pt": "Outra coisa"
                        },
                        "offer_id": null,
                        "free_text": true
                      }
                    ],
                    "feedback": {
                      "enabled": true,
                      "required": false
                    },
                    "cancel_mode": "at_period_end",
                    "review_requests": {
                      "enabled": false,
                      "happy_reasons": [
                        "project_finished",
                        "too_expensive",
                        "temporary"
                      ],
                      "follow_up_email_days": 2
                    },
                    "offers_once_per_customer_per_year": true
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          },
          "404": {
            "description": "another account's logo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/cancel_flows/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "description": "default or flw_…",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "locale",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Cancel flow config for the widget or a native app",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "getCancelFlow",
        "responses": {
          "200": {
            "description": "texts in the customer's language, reasons, theme",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_flow",
                  "id": "flw_EmdD2FFD6aiqGNqz",
                  "published": true,
                  "version": 1,
                  "locale": "de",
                  "texts": {
                    "review_ask": "Würden Sie eine kurze Bewertung auf {{platform}} hinterlassen? Keine Belohnung, nur Ihre ehrliche Meinung.",
                    "review_cta": "Bewertung schreiben",
                    "title": "Bevor Sie gehen",
                    "reason_prompt": "Warum kündigen Sie?",
                    "other_placeholder": "Erzählen Sie uns mehr (optional)",
                    "offer_title": "Eine letzte Sache",
                    "accept": "Angebot annehmen",
                    "decline": "Nein danke, weiter",
                    "feedback_prompt": "Was hätten wir besser machen können?",
                    "feedback_submit": "Weiter",
                    "confirm_title": "Abonnement kündigen?",
                    "confirm_body": "Sie behalten den Zugang bis zum Ende des bezahlten Zeitraums.",
                    "cancel_now": "Mein Abonnement kündigen",
                    "saved_title": "Danke, dass Sie bleiben!",
                    "cancelled_title": "Ihr Abonnement ist gekündigt",
                    "cancelled_body": "Sie behalten den Zugang bis zum Ende des bezahlten Zeitraums.",
                    "offer_discount": "{{percent}} % Rabatt für {{months}} Monate",
                    "offer_pause": "{{months}} Monate kostenlos pausieren",
                    "offer_downgrade": "Zu {{plan}} wechseln",
                    "offer_talk": "Mit dem Team sprechen",
                    "choose_subscription_title": "Welches Abonnement?",
                    "choose_subscription_body": "Sie haben mehrere Abonnements. Wählen Sie das, das Sie kündigen möchten.",
                    "choose_subscription_select": "Mit diesem fortfahren",
                    "cancel_this_one": "Dieses kündigen",
                    "renews_on": "Verlängert sich am {{date}}"
                  },
                  "reasons": [
                    {
                      "key": "too_expensive",
                      "label": "Es ist zu teuer",
                      "free_text": false
                    },
                    {
                      "key": "missing_feature",
                      "label": "Eine Funktion fehlt",
                      "free_text": false
                    },
                    {
                      "key": "not_using",
                      "label": "Ich nutze es zu wenig",
                      "free_text": false
                    },
                    {
                      "key": "switching",
                      "label": "Ich wechsle zu einem anderen Tool",
                      "free_text": false
                    },
                    {
                      "key": "temporary",
                      "label": "Ich brauche nur eine Pause",
                      "free_text": false
                    },
                    {
                      "key": "project_finished",
                      "label": "Mein Projekt ist abgeschlossen",
                      "free_text": false
                    },
                    {
                      "key": "other",
                      "label": "Etwas anderes",
                      "free_text": true
                    }
                  ],
                  "feedback_enabled": true,
                  "feedback": {
                    "enabled": true,
                    "required": false
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Acme",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "languages": [
                    "en",
                    "fr",
                    "de",
                    "it",
                    "pt"
                  ],
                  "cancel_fallback_url": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelFlow"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/cancel_sessions": {
      "post": {
        "summary": "Open the cancel flow for a customer",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "createCancelSession",
        "description": "From the browser: publishable key + X-NoChurn-Customer-Token + allowed Origin. From a server: secret key and customer. Click to cancel: POST /cancel works at every step; cancel_url_now is shown once. The subscription it targets: the token's `subscription` claim (browser) or `subscription` (server), else the customer's only live one; several and none named: the session starts at choose_subscription (DECISIONS 72).",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "session at the reason step",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_session",
                  "id": "ses_CWSSquPBUBep38LF",
                  "livemode": false,
                  "step": "reason",
                  "outcome": null,
                  "locale": "en",
                  "holdout": false,
                  "published": true,
                  "customer": {
                    "id": "cst_8spCLa6W9qQzhEpt",
                    "stripe_customer_id": "cus_1",
                    "external_id": "user_42"
                  },
                  "subscription": {
                    "id": "nsub_dNjp6zubp5wmUDhA",
                    "plan": null,
                    "status": "active",
                    "amount_cents": 4900,
                    "currency": "eur",
                    "interval": "month",
                    "interval_count": 1,
                    "current_period_end": "2026-11-24T10:00:00Z",
                    "cancel_at_period_end": false
                  },
                  "subscriptions": [],
                  "reasons": [
                    {
                      "key": "too_expensive",
                      "label": "It's too expensive",
                      "free_text": false
                    },
                    {
                      "key": "missing_feature",
                      "label": "A feature is missing",
                      "free_text": false
                    },
                    {
                      "key": "not_using",
                      "label": "I don't use it enough",
                      "free_text": false
                    },
                    {
                      "key": "switching",
                      "label": "I'm switching to another tool",
                      "free_text": false
                    },
                    {
                      "key": "temporary",
                      "label": "I only need a break",
                      "free_text": false
                    },
                    {
                      "key": "project_finished",
                      "label": "My project is finished",
                      "free_text": false
                    },
                    {
                      "key": "other",
                      "label": "Something else",
                      "free_text": true
                    }
                  ],
                  "offer": null,
                  "talk_url": null,
                  "texts": {
                    "review_ask": "Would you leave a short review on {{platform}}? No reward, just your honest opinion.",
                    "review_cta": "Leave a review",
                    "title": "Before you go",
                    "reason_prompt": "Why are you cancelling?",
                    "other_placeholder": "Tell us more (optional)",
                    "offer_title": "One last thing",
                    "accept": "Accept the offer",
                    "decline": "No thanks, continue",
                    "feedback_prompt": "Anything we could have done better?",
                    "feedback_submit": "Continue",
                    "confirm_title": "Cancel your subscription?",
                    "confirm_body": "You keep access until the end of the paid period.",
                    "cancel_now": "Cancel my subscription",
                    "saved_title": "Thanks for staying!",
                    "cancelled_title": "Your subscription is cancelled",
                    "cancelled_body": "You keep access until the end of the paid period.",
                    "offer_discount": "{{percent}}% off for {{months}} months",
                    "offer_pause": "Pause for {{months}} months, free",
                    "offer_downgrade": "Switch to {{plan}}",
                    "offer_talk": "Talk to the team",
                    "choose_subscription_title": "Which subscription?",
                    "choose_subscription_body": "You have several subscriptions. Choose the one you want to cancel.",
                    "choose_subscription_select": "Continue with this one",
                    "cancel_this_one": "Cancel this one",
                    "renews_on": "Renews on {{date}}"
                  },
                  "feedback_enabled": true,
                  "feedback": {
                    "enabled": true,
                    "required": false
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Acme",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "cancel_url_now": "http://localhost:3000/x/Il4clEDtUMUqj9it2-O37qsHnhoIWbq8",
                  "cancel_status": null,
                  "cancel_effective_at": null,
                  "result": null,
                  "access_until": null,
                  "confirmation_email_sent": false,
                  "review": null,
                  "expires_at": "2026-11-05T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelSession"
                }
              }
            }
          },
          "401": {
            "description": "a publishable key without a customer token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "an origin that is not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "customer": {
                    "type": "string",
                    "example": "user_42"
                  },
                  "subscription": {
                    "type": "string",
                    "example": "sub_1PqX2",
                    "description": "Secret key only (from a browser: the token's `subscription` claim). nsub_…, sub_… or its id in your billing; a live subscription of that customer, else 404 subscription_not_found."
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "fr",
                      "de",
                      "it",
                      "pt"
                    ]
                  }
                }
              },
              "example": {
                "customer": "user_42"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "Cancel sessions",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "listCancelSessions",
        "parameters": [
          {
            "name": "outcome",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "saved",
                "cancelled",
                "abandoned"
              ]
            }
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "newest first",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "cancel_session",
                      "id": "ses_XsgUyosnL9TPcZiz",
                      "livemode": false,
                      "step": "reason",
                      "outcome": null,
                      "locale": "en",
                      "holdout": false,
                      "published": false,
                      "customer": {
                        "id": "cst_H5m8w5V7ovfoLPQE",
                        "stripe_customer_id": null,
                        "external_id": null
                      },
                      "subscription": null,
                      "subscriptions": [],
                      "reasons": [
                        {
                          "key": "too_expensive",
                          "label": "It's too expensive",
                          "free_text": false
                        },
                        {
                          "key": "missing_feature",
                          "label": "A feature is missing",
                          "free_text": false
                        },
                        {
                          "key": "not_using",
                          "label": "I don't use it enough",
                          "free_text": false
                        },
                        {
                          "key": "switching",
                          "label": "I'm switching to another tool",
                          "free_text": false
                        },
                        {
                          "key": "temporary",
                          "label": "I only need a break",
                          "free_text": false
                        },
                        {
                          "key": "project_finished",
                          "label": "My project is finished",
                          "free_text": false
                        },
                        {
                          "key": "other",
                          "label": "Something else",
                          "free_text": true
                        }
                      ],
                      "offer": null,
                      "talk_url": null,
                      "texts": {
                        "review_ask": "Would you leave a short review on {{platform}}? No reward, just your honest opinion.",
                        "review_cta": "Leave a review",
                        "title": "Before you go",
                        "reason_prompt": "Why are you cancelling?",
                        "other_placeholder": "Tell us more (optional)",
                        "offer_title": "One last thing",
                        "accept": "Accept the offer",
                        "decline": "No thanks, continue",
                        "feedback_prompt": "Anything we could have done better?",
                        "feedback_submit": "Continue",
                        "confirm_title": "Cancel your subscription?",
                        "confirm_body": "You keep access until the end of the paid period.",
                        "cancel_now": "Cancel my subscription",
                        "saved_title": "Thanks for staying!",
                        "cancelled_title": "Your subscription is cancelled",
                        "cancelled_body": "You keep access until the end of the paid period.",
                        "offer_discount": "{{percent}}% off for {{months}} months",
                        "offer_pause": "Pause for {{months}} months, free",
                        "offer_downgrade": "Switch to {{plan}}",
                        "offer_talk": "Talk to the team",
                        "choose_subscription_title": "Which subscription?",
                        "choose_subscription_body": "You have several subscriptions. Choose the one you want to cancel.",
                        "choose_subscription_select": "Continue with this one",
                        "cancel_this_one": "Cancel this one",
                        "renews_on": "Renews on {{date}}"
                      },
                      "feedback_enabled": true,
                      "feedback": {
                        "enabled": true,
                        "required": false
                      },
                      "theme": {
                        "object": "cancel_flow_theme",
                        "preset": "light",
                        "format": "modal",
                        "tokens": {
                          "bg": "#f5f6fa",
                          "surface": "#ffffff",
                          "text": "#1c1e4a",
                          "muted": "#686b85",
                          "accent": "#4637d9",
                          "accent_text": "#ffffff",
                          "border": "#e3e5ec",
                          "danger": "#c2410c",
                          "font": "system-ui, sans-serif",
                          "font_size": 15,
                          "radius": 12,
                          "radius_control": 8,
                          "overlay": 0.5,
                          "max_width": 480,
                          "shadow": "md",
                          "spacing": "regular",
                          "border_width": 1,
                          "border_style": "solid",
                          "font_scale": 1.25,
                          "font_weight_title": 600
                        },
                        "font_url": null,
                        "brand": "logo",
                        "logo_url": null,
                        "app_name": "Account 1",
                        "end_screen": {
                          "style": "celebrate",
                          "auto_close_seconds": 6
                        },
                        "custom_css": "",
                        "cancel_button_check": "ok",
                        "classes": [
                          "nc-modal",
                          "nc-title",
                          "nc-reason",
                          "nc-offer",
                          "nc-btn-primary",
                          "nc-btn-cancel",
                          "nc-footer"
                        ]
                      },
                      "cancel_url_now": null,
                      "cancel_status": null,
                      "cancel_effective_at": null,
                      "result": null,
                      "access_until": null,
                      "confirmation_email_sent": false,
                      "review": null,
                      "expires_at": "2026-11-05T10:00:00Z",
                      "created_at": "2026-11-04T10:00:00Z",
                      "reason": "too_expensive",
                      "comment": "Pricey"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelSessionReportList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/offers": {
      "post": {
        "summary": "Add an offer to the cancel flow's library",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "createOffer",
        "parameters": [],
        "responses": {
          "201": {
            "description": "a discount that names a Maxio coupon",
            "content": {
              "application/json": {
                "example": {
                  "object": "offer",
                  "id": "off_9QeL5KwSdZsZnnwJ",
                  "kind": "discount",
                  "name": "30% for 3 months",
                  "params": {
                    "percent": 30,
                    "months": 3
                  },
                  "archived_at": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          },
          "422": {
            "description": "a talk offer without an https link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "name",
                  "params"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "discount",
                      "pause",
                      "downgrade",
                      "talk"
                    ]
                  },
                  "name": {
                    "type": "string"
                  },
                  "params": {
                    "type": "object",
                    "description": "discount { percent, months, coupon_code? (Maxio coupon) } · pause { months } · downgrade { target_price_id, plan_name } · talk { url }"
                  }
                }
              },
              "example": {
                "kind": "discount",
                "name": "30% for 3 months",
                "params": {
                  "percent": 30,
                  "months": 3
                }
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "Offer library",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "listOffers",
        "parameters": [
          {
            "name": "include_archived",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "active offers",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "offer",
                      "id": "off_KmCAXbU2dJBckeAo",
                      "kind": "discount",
                      "name": "30% for 3 months",
                      "params": {
                        "months": 3,
                        "percent": 30
                      },
                      "archived_at": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/OfferList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/account/clients": {
      "get": {
        "summary": "Versions of NoChurn's client code calling the API",
        "tags": [
          "Account"
        ],
        "operationId": "listClientVersions",
        "description": "Versions of NoChurn's own client code (the widget js.nochurn.sh, the MCP server, the SDKs) seen in the last 90 days in this mode, from the NoChurn-Client header (`<name>/<semver>`), per client with its latest and minimum supported version. Row status: current, update_available (below latest), unsupported (below minimum), unknown (a client NoChurn does not publish). The widget's origin_host is the host of the page that loaded it; api_key is the key used, oauth_client the AI app (MCP through OAuth). Rows are written at most once every 5 minutes: last_seen_at is within 5 minutes and calls_count is a lower bound. Resource account (read), every role. js.nochurn.sh/v1 updates itself within a major version: update_available for the widget means a pinned /v1.x.y.js URL, a self-hosted copy or an older major.",
        "responses": {
          "200": {
            "description": "the clients and their versions",
            "content": {
              "application/json": {
                "example": {
                  "object": "client_versions",
                  "livemode": false,
                  "window_days": 90,
                  "data": [
                    {
                      "object": "client",
                      "client": "widget",
                      "name": "Cancel widget (js.nochurn.sh)",
                      "latest": "1.0.0",
                      "minimum": "1.0.0",
                      "status": "unsupported",
                      "versions": [
                        {
                          "id": "clv_fv7a8FvU8CUJZJ8F",
                          "version": "1.0.0",
                          "status": "current",
                          "origin_host": "app.acme.com",
                          "api_key": {
                            "id": "key_ytLQLbZVZ2GdVyUF",
                            "name": "Key 1",
                            "kind": "publishable",
                            "mode": "test",
                            "last4": "ntgP"
                          },
                          "oauth_client": null,
                          "first_seen_at": "2026-11-04T09:00:00.000Z",
                          "last_seen_at": "2026-11-04T09:00:00.000Z",
                          "calls_count": 3
                        },
                        {
                          "id": "clv_nRfcm9YjNjXUcMqC",
                          "version": "0.9.0",
                          "status": "unsupported",
                          "origin_host": "app.acme.com",
                          "api_key": {
                            "id": "key_ytLQLbZVZ2GdVyUF",
                            "name": "Key 1",
                            "kind": "publishable",
                            "mode": "test",
                            "last4": "ntgP"
                          },
                          "oauth_client": null,
                          "first_seen_at": "2026-11-02T10:00:00.000Z",
                          "last_seen_at": "2026-11-02T10:00:00.000Z",
                          "calls_count": 3
                        }
                      ]
                    },
                    {
                      "object": "client",
                      "client": "mcp",
                      "name": "NoChurn MCP server",
                      "latest": "0.1.0",
                      "minimum": "0.1.0",
                      "status": "current",
                      "versions": [
                        {
                          "id": "clv_E1d1eodQeZi9yoUJ",
                          "version": "0.1.0",
                          "status": "current",
                          "origin_host": null,
                          "api_key": null,
                          "oauth_client": {
                            "id": "mcpc_EioDePP8sJxkMntr",
                            "name": "Claude Code"
                          },
                          "first_seen_at": "2026-11-04T09:00:00.000Z",
                          "last_seen_at": "2026-11-04T09:00:00.000Z",
                          "calls_count": 3
                        }
                      ]
                    },
                    {
                      "object": "client",
                      "client": "node",
                      "name": null,
                      "latest": null,
                      "minimum": null,
                      "status": "unknown",
                      "versions": [
                        {
                          "id": "clv_L5QU6Xo2vi6b7TLV",
                          "version": "1.2.0",
                          "status": "unknown",
                          "origin_host": null,
                          "api_key": {
                            "id": "key_HCwE3oWH5fUAkZ91",
                            "name": "Server",
                            "kind": "secret",
                            "mode": "test",
                            "last4": "51K6"
                          },
                          "oauth_client": null,
                          "first_seen_at": "2026-11-04T09:00:00.000Z",
                          "last_seen_at": "2026-11-04T09:00:00.000Z",
                          "calls_count": 3
                        }
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/ClientVersions"
                }
              }
            }
          },
          "401": {
            "description": "no credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/access": {
      "get": {
        "summary": "May this customer use your app? (server-side wall)",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomerAccess",
        "description": "The truth behind NoChurn's in-app wall, for your backend: the widget's wall alone can be bypassed. Same calculation as GET /v1/inapp/state (wall.after_days, annual_grace_days; 0 = as soon as the payment fails). Secret key (customers: read). Webhooks customer.access_restricted / customer.access_restored tell you when it changes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "cst_…, cus_… or your id."
          },
          {
            "name": "stripe_account",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "acct_… when the same cus_ exists on two connected Stripe accounts."
          }
        ],
        "responses": {
          "200": {
            "description": "paid: full, nothing to pay",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_access",
                  "customer": "cst_4w9Nj8EJEvWj7MDT",
                  "livemode": false,
                  "access": "full",
                  "reason": null,
                  "since": null,
                  "failing_since": "2026-11-04T10:00:00Z",
                  "restricts_at": "2026-11-11T10:00:00Z",
                  "invoice": "ninv_1dMMAnaZyLmBNtDB",
                  "pay_url": "http://localhost:3000/c/Iyp0n60Dcd6R6xAK3UJ1MszddKUzHkeP",
                  "subscription": null,
                  "paused_at": null,
                  "resumes_at": null,
                  "cancel_url": "http://localhost:3000/a/eyJfcmFpbHMiOnsiZGF0YSI6ImNzdF80dzlOajhFSkV2V2o3TURUIiwiZXhwIjoiMjAyNi0xMi0wNFQxMDowMDowMC4wMDBaIiwicHVyIjoiY3VzdG9tZXIvYWNjZXNzX2NhbmNlbCJ9fQ--46a89957572650047e29db93b65f0dc84d316720d722daa2cb2615d5fafc99f9"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerAccess"
                }
              }
            }
          },
          "404": {
            "description": "customer of another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/summary": {
      "get": {
        "summary": "The 4 figures of the Customers page",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomerSummary",
        "description": "This month starts on the 1st in the account's time zone. Amounts are grouped by currency.",
        "responses": {
          "200": {
            "description": "summary",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_summary",
                  "livemode": false,
                  "timezone": "Europe/Paris",
                  "month_start": "2026-11-01T00:00:00+01:00",
                  "active": {
                    "count": 1,
                    "new_this_month": 1
                  },
                  "high_risk": {
                    "threshold": 70,
                    "count": 1,
                    "mrr": [
                      {
                        "currency": "eur",
                        "cents": 4900
                      }
                    ],
                    "scored_customers": 1
                  },
                  "in_recovery": {
                    "count": 1,
                    "mrr": [
                      {
                        "currency": "eur",
                        "cents": 4900
                      }
                    ]
                  },
                  "saved_this_month": {
                    "count": null,
                    "unavailable_reason": "Counted from the recovery and cancel flows, not live yet."
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerSummary"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/profile": {
      "get": {
        "summary": "Customer profile",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomerProfile",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "cst_…, cus_… or your id."
          }
        ],
        "responses": {
          "200": {
            "description": "profile",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_profile",
                  "customer": {
                    "object": "customer",
                    "id": "cst_6KWhRc2vsEuErrbU",
                    "stripe_customer_id": "cus_lena",
                    "external_id": "u_lena",
                    "stripe_account": "acct_A",
                    "livemode": false,
                    "email": "customer1@acme-corp.dev",
                    "company_domain": "acme-corp.dev",
                    "name": null,
                    "locale": null,
                    "country": null,
                    "region": null,
                    "timezone": "Europe/Paris",
                    "timezone_source": "account",
                    "attributes": {
                      "plan": "pro",
                      "exports_30d": 46
                    },
                    "churn_score": 82,
                    "churn_level": "high",
                    "flag": null,
                    "state": "in_recovery",
                    "plan": "Pro",
                    "mrr_cents": 4900,
                    "lifetime_value_cents": 4900,
                    "currency": "eur",
                    "payment_method": {
                      "id": "npm_5g6sqZdgrBnJo1rw",
                      "type": "card",
                      "brand": "visa",
                      "last4": "4242",
                      "exp_month": null,
                      "exp_year": null,
                      "funding": "prepaid",
                      "wallet": "apple_pay",
                      "risk_category": "prepaid",
                      "label": "Apple Pay · Visa •••• 4242"
                    },
                    "last_seen_at": null,
                    "churned_at": null,
                    "flows_paused_at": null,
                    "signup_at": null,
                    "created_at": "2026-11-04T10:00:00.000Z",
                    "updated_at": "2026-11-04T10:00:00.000Z",
                    "identifiers": []
                  },
                  "billing": {
                    "mrr_cents": 4900,
                    "currency": "eur",
                    "lifetime_value_cents": 4900,
                    "lifetime_value_since": "2025-11-04T10:00:00.000Z",
                    "customer_since": "2026-11-04T10:00:00.000Z",
                    "payment_method": {
                      "id": "npm_5g6sqZdgrBnJo1rw",
                      "type": "card",
                      "brand": "visa",
                      "last4": "4242",
                      "exp_month": null,
                      "exp_year": null,
                      "funding": "prepaid",
                      "wallet": "apple_pay",
                      "risk_category": "prepaid",
                      "label": "Apple Pay · Visa •••• 4242"
                    },
                    "next_renewal": {
                      "at": "2026-11-14T10:00:00.000Z",
                      "amount_cents": 4900,
                      "currency": "eur",
                      "subscription": "nsub_9mqhquow11EnC1Px"
                    },
                    "subscriptions": [
                      {
                        "id": "nsub_9mqhquow11EnC1Px",
                        "plan": "Pro",
                        "status": "active",
                        "mrr_cents": 4900,
                        "currency": "eur",
                        "interval": "month",
                        "interval_count": 1,
                        "current_period_end": "2026-11-14T10:00:00.000Z",
                        "cancel_at_period_end": false
                      }
                    ]
                  },
                  "score": {
                    "score": 82,
                    "level": "high",
                    "factors": [],
                    "computed_at": null
                  },
                  "ai_analysis": null,
                  "ai_analysis_status": "missing",
                  "attributes": {
                    "plan": "pro",
                    "exports_30d": 46
                  },
                  "links": {
                    "stripe_dashboard": "https://dashboard.stripe.com/acct_A/test/customers/cus_lena"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerProfile"
                }
              }
            }
          },
          "404": {
            "description": "customer of another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/timeline": {
      "get": {
        "summary": "Customer activity, newest first",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomerTimeline",
        "description": "NoChurn events, payment attempts, subscription starts and cancellations.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "second page",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "timeline_item",
                      "id": "nevt_TCnRkbneCoz1xsac",
                      "occurred_at": "2026-11-04T10:00:00.000Z",
                      "category": "billing",
                      "type": "payment_method.attached",
                      "summary": "Card added",
                      "ref": {
                        "type": "payment_method",
                        "id": "npm_4VZffAUJcm7uFR8g"
                      },
                      "data": {}
                    },
                    {
                      "object": "timeline_item",
                      "id": "npay_EpsfXBzWuJC66pWy",
                      "occurred_at": "2026-11-03T10:00:00.000Z",
                      "category": "billing",
                      "type": "payment.failed",
                      "summary": "€49.00 · failed · insufficient_funds",
                      "ref": {
                        "type": "payment",
                        "id": "npay_EpsfXBzWuJC66pWy"
                      },
                      "data": {
                        "amount_cents": 4900,
                        "amount_refunded_cents": 0,
                        "currency": "eur",
                        "decline_code": "insufficient_funds",
                        "risk_category": null,
                        "invoice": null
                      }
                    },
                    {
                      "object": "timeline_item",
                      "id": "nsub_wwcVS74Uv3QGxhqn.started",
                      "occurred_at": "2026-08-04T10:00:00.000Z",
                      "category": "billing",
                      "type": "subscription.started",
                      "summary": "Pro · active",
                      "ref": {
                        "type": "subscription",
                        "id": "nsub_wwcVS74Uv3QGxhqn"
                      },
                      "data": {
                        "plan": "Pro",
                        "status": "active",
                        "mrr_cents": 4900,
                        "currency": "eur"
                      }
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/TimelinePage"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/pause_flows": {
      "post": {
        "summary": "Pause all flows for a customer",
        "tags": [
          "Customers"
        ],
        "operationId": "pauseCustomerFlows",
        "description": "Stops the customer's active runs and blocks new ones until resume_flows.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "paused",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_8VVgYZN5XyepV47K",
                  "stripe_customer_id": "cus_lena",
                  "external_id": "u_lena",
                  "stripe_account": "acct_A",
                  "livemode": false,
                  "email": "customer1@acme-corp.dev",
                  "company_domain": "acme-corp.dev",
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {
                    "plan": "pro",
                    "exports_30d": 46
                  },
                  "churn_score": 82,
                  "churn_level": "high",
                  "flag": null,
                  "state": "in_recovery",
                  "plan": "Pro",
                  "mrr_cents": 4900,
                  "lifetime_value_cents": 4900,
                  "currency": "eur",
                  "payment_method": {
                    "id": "npm_2HFV26kN2xmX77qn",
                    "type": "card",
                    "brand": "visa",
                    "last4": "4242",
                    "exp_month": null,
                    "exp_year": null,
                    "funding": "prepaid",
                    "wallet": "apple_pay",
                    "risk_category": "prepaid",
                    "label": "Apple Pay · Visa •••• 4242"
                  },
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": "2026-11-04T10:00:00.000Z",
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/resume_flows": {
      "post": {
        "summary": "Resume flows for a customer",
        "tags": [
          "Customers"
        ],
        "operationId": "resumeCustomerFlows",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "resumed",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_yg7PWhJh6a5aucuT",
                  "stripe_customer_id": "cus_lena",
                  "external_id": "u_lena",
                  "stripe_account": "acct_A",
                  "livemode": false,
                  "email": "customer1@acme-corp.dev",
                  "company_domain": "acme-corp.dev",
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {
                    "plan": "pro",
                    "exports_30d": 46
                  },
                  "churn_score": 82,
                  "churn_level": "high",
                  "flag": null,
                  "state": "in_recovery",
                  "plan": "Pro",
                  "mrr_cents": 4900,
                  "lifetime_value_cents": 4900,
                  "currency": "eur",
                  "payment_method": {
                    "id": "npm_QWF1hjeTz5iFPdTE",
                    "type": "card",
                    "brand": "visa",
                    "last4": "4242",
                    "exp_month": null,
                    "exp_year": null,
                    "funding": "prepaid",
                    "wallet": "apple_pay",
                    "risk_category": "prepaid",
                    "label": "Apple Pay · Visa •••• 4242"
                  },
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customer_duplicates/summary": {
      "get": {
        "summary": "Possible duplicates: count and scans",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomerDuplicatesSummary",
        "description": "Open proposals of this mode, the end of the last scan and the latest scan and merge all, with their real counts.",
        "responses": {
          "200": {
            "description": "summary of the current mode",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_duplicate_summary",
                  "livemode": false,
                  "open_count": 1,
                  "last_scanned_at": "2026-11-04T10:00:00.000Z",
                  "scan": {
                    "object": "customer_duplicate_run",
                    "id": "cdr_pvVTkKp4mB7Aoxqo",
                    "livemode": false,
                    "kind": "scan",
                    "status": "done",
                    "total": 2,
                    "processed": 2,
                    "found": 1,
                    "merged": 0,
                    "skipped": 0,
                    "failed": 0,
                    "failures": [],
                    "error": null,
                    "started_at": "2026-11-04T10:00:00.000Z",
                    "finished_at": "2026-11-04T10:00:00.000Z",
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "merge_all": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicateSummary"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customer_duplicates/scan": {
      "post": {
        "summary": "Scan the customers for possible duplicates",
        "tags": [
          "Customers"
        ],
        "operationId": "scanCustomerDuplicates",
        "description": "Looks for possible duplicates among the customers of this mode already in base, with the same rules as customer matching (a shared current email; an id one customer holds that is another one's alias). In the background: poll GET /v1/customer_duplicates/summary. A pair already proposed, dismissed or merged is never proposed again. One at a time (a running scan is answered), once a minute at most (429). Owner or admin.",
        "responses": {
          "202": {
            "description": "scan queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_duplicate_run",
                  "id": "cdr_dSZC8EKunhNUWQrf",
                  "livemode": false,
                  "kind": "scan",
                  "status": "queued",
                  "total": 0,
                  "processed": 0,
                  "found": 0,
                  "merged": 0,
                  "skipped": 0,
                  "failed": 0,
                  "failures": [],
                  "error": null,
                  "started_at": null,
                  "finished_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicateRun"
                }
              }
            }
          },
          "429": {
            "description": "scanned less than a minute ago",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customer_duplicates/merge_all": {
      "post": {
        "summary": "Merge every open possible duplicate",
        "tags": [
          "Customers"
        ],
        "operationId": "mergeAllCustomerDuplicates",
        "description": "Irreversible. Merges each open proposal of this mode into its suggested_keep customer, one merge per pair (same as POST /v1/customer_duplicates/{id}/merge), oldest first: in a chain (A~B, B~C) the second merge targets the customer kept by the first. A pair that fails stays open with its reason in failures; the others go on. Up to 10 pairs: merged now (200); more: in the background (202, poll the summary). While one runs, it is answered. Owner or admin.",
        "responses": {
          "200": {
            "description": "merged now",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_duplicate_run",
                  "id": "cdr_CfHJreAxeF3b8VVy",
                  "livemode": false,
                  "kind": "merge_all",
                  "status": "done",
                  "total": 1,
                  "processed": 1,
                  "found": 0,
                  "merged": 1,
                  "skipped": 0,
                  "failed": 0,
                  "failures": [],
                  "error": null,
                  "started_at": "2026-11-04T10:00:00.000Z",
                  "finished_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicateRun"
                }
              }
            }
          },
          "202": {
            "description": "many pairs: in the background",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_duplicate_run",
                  "id": "cdr_JM2D5yebmkpCcrDU",
                  "livemode": false,
                  "kind": "merge_all",
                  "status": "queued",
                  "total": 12,
                  "processed": 0,
                  "found": 0,
                  "merged": 0,
                  "skipped": 0,
                  "failed": 0,
                  "failures": [],
                  "error": null,
                  "started_at": null,
                  "finished_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicateRun"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers": {
      "get": {
        "summary": "List customers",
        "tags": [
          "Customers"
        ],
        "operationId": "listCustomers",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Search email, name or any id, including every identifier a source gave (a tool's id, a previous email or id)."
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Current or previous email."
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Churn level, comma-separated: low, medium, high."
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated: none, trialing, active, in_recovery, paused, churned."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "-churn_score",
                "-mrr",
                "-last_seen_at"
              ],
              "default": "-created_at"
            },
            "description": "Descending. Customers without a score (or last_seen_at) come last."
          },
          {
            "name": "at_risk",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true: a churn score of 70 or more, or an early signal (flag: card removed…), the rule of Home's at-risk renewals; false: the others."
          },
          {
            "name": "renews_within_days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90
            },
            "description": "A paying subscription (active or past_due) whose current period ends within this many days (set to cancel at the period end included: include[]=risk says so). With at_risk=true and 7: Home's at_risk_7d."
          },
          {
            "name": "include[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "total_count",
                  "risk"
                ]
              }
            },
            "description": "total_count: count of the filtered list. risk: each customer's next renewal and score factors (risk)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "at risk and renewing within 7 days, with why and when",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "customer",
                      "id": "cst_bTjTg5h1W9jXomCB",
                      "stripe_customer_id": "cus_123",
                      "external_id": null,
                      "stripe_account": "acct_A",
                      "livemode": false,
                      "email": "ana@example.test",
                      "company_domain": null,
                      "name": null,
                      "locale": null,
                      "country": null,
                      "region": null,
                      "timezone": "Europe/Paris",
                      "timezone_source": "account",
                      "attributes": {},
                      "churn_score": null,
                      "churn_level": null,
                      "flag": null,
                      "state": "none",
                      "plan": null,
                      "mrr_cents": 0,
                      "lifetime_value_cents": 0,
                      "currency": null,
                      "payment_method": null,
                      "last_seen_at": null,
                      "churned_at": null,
                      "flows_paused_at": null,
                      "signup_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z",
                      "updated_at": "2026-11-04T10:00:00.000Z",
                      "identifiers": []
                    },
                    {
                      "object": "customer",
                      "id": "cst_BMdHx29aVWcEdQLp",
                      "stripe_customer_id": null,
                      "external_id": "u_123",
                      "stripe_account": null,
                      "livemode": false,
                      "email": "bob@example.test",
                      "company_domain": null,
                      "name": null,
                      "locale": null,
                      "country": null,
                      "region": null,
                      "timezone": "Europe/Paris",
                      "timezone_source": "account",
                      "attributes": {},
                      "churn_score": null,
                      "churn_level": null,
                      "flag": null,
                      "state": "none",
                      "plan": null,
                      "mrr_cents": 0,
                      "lifetime_value_cents": 0,
                      "currency": null,
                      "payment_method": null,
                      "last_seen_at": null,
                      "churned_at": null,
                      "flows_paused_at": null,
                      "signup_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z",
                      "updated_at": "2026-11-04T10:00:00.000Z",
                      "identifiers": []
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerPage"
                }
              }
            }
          },
          "400": {
            "description": "renews_within_days out of range",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Create a customer (billing without Stripe, or before Stripe knows it)",
        "tags": [
          "Customers"
        ],
        "operationId": "createCustomer",
        "description": "Matched first (customer matching): when exactly one customer of this mode has this email and no other id of yours, it is linked to your external_id and returned with 200 instead of a duplicate. Several customers with the email: a new one is created and a possible duplicate To do is raised.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "the only customer with this email, linked to your external_id",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_pzfcpuSfUFQSGkNf",
                  "stripe_customer_id": "cus_123",
                  "external_id": "u_ana",
                  "stripe_account": "acct_A",
                  "livemode": false,
                  "email": "ana@example.test",
                  "company_domain": null,
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {},
                  "churn_score": null,
                  "churn_level": null,
                  "flag": null,
                  "state": "none",
                  "plan": null,
                  "mrr_cents": 0,
                  "lifetime_value_cents": 0,
                  "currency": null,
                  "payment_method": null,
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": [
                    {
                      "kind": "email",
                      "provider": null,
                      "connection": null,
                      "value": "ana@example.test",
                      "source": "api",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "kind": "external",
                      "provider": null,
                      "connection": null,
                      "value": "u_ana",
                      "source": "api",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "201": {
            "description": "created",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_ATdWVXLNrWdPxkSw",
                  "stripe_customer_id": null,
                  "external_id": "u_456",
                  "stripe_account": null,
                  "livemode": false,
                  "email": "new@example.test",
                  "company_domain": null,
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {},
                  "churn_score": null,
                  "churn_level": null,
                  "flag": null,
                  "state": "none",
                  "plan": null,
                  "mrr_cents": 0,
                  "lifetime_value_cents": 0,
                  "currency": null,
                  "payment_method": null,
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": [
                    {
                      "kind": "external",
                      "provider": null,
                      "connection": null,
                      "value": "u_456",
                      "source": "api",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "kind": "email",
                      "provider": null,
                      "connection": null,
                      "value": "new@example.test",
                      "source": "api",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "no identifier",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "external_id already used",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "external_id": {
                    "type": "string",
                    "description": "Recommended: the id in your app."
                  },
                  "email": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "de",
                      "fr",
                      "it",
                      "pt"
                    ]
                  },
                  "country": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string",
                    "description": "State or province (US: TX)."
                  },
                  "timezone": {
                    "type": "string",
                    "nullable": true,
                    "description": "IANA zone. Wins over any guess; null goes back to the guess."
                  }
                }
              },
              "example": {
                "external_id": "u_456",
                "email": "new@example.test"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/customers/resolve": {
      "get": {
        "summary": "Resolve a customer from any of its ids",
        "tags": [
          "Customers"
        ],
        "operationId": "resolveCustomer",
        "description": "Pass exactly one of customer (cst_…, cus_… or your id, resolved in that order), stripe_customer or external_id.",
        "parameters": [
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stripe_customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stripe_account",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "acct_… — needed when the same cus_ exists on two connected Stripe accounts."
          }
        ],
        "responses": {
          "200": {
            "description": "the customer and its three ids",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_ssJU64rijfqjh2XS",
                  "stripe_customer_id": null,
                  "external_id": "u_123",
                  "stripe_account": null,
                  "livemode": false,
                  "email": "bob@example.test",
                  "company_domain": null,
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {},
                  "churn_score": null,
                  "churn_level": null,
                  "flag": null,
                  "state": "none",
                  "plan": null,
                  "mrr_cents": 0,
                  "lifetime_value_cents": 0,
                  "currency": null,
                  "payment_method": null,
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "404": {
            "description": "no match",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "ambiguous",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "description": "cst_…, cus_… or your own id.",
          "required": true
        },
        {
          "name": "stripe_account",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Retrieve a customer",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomer",
        "responses": {
          "200": {
            "description": "customer",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_3WcGvYMRCF8rixYt",
                  "stripe_customer_id": "cus_123",
                  "external_id": null,
                  "stripe_account": "acct_A",
                  "livemode": false,
                  "email": "ana@example.test",
                  "company_domain": null,
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {},
                  "churn_score": null,
                  "churn_level": null,
                  "flag": null,
                  "state": "none",
                  "plan": null,
                  "mrr_cents": 0,
                  "lifetime_value_cents": 0,
                  "currency": null,
                  "payment_method": null,
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "404": {
            "description": "not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Update a customer — e.g. link your own id to a Stripe customer",
        "tags": [
          "Customers"
        ],
        "operationId": "updateCustomer",
        "parameters": [],
        "responses": {
          "200": {
            "description": "time zone set by your app, then back to the guess",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_QwoaoX5sv4eucKBL",
                  "stripe_customer_id": "cus_123",
                  "external_id": "u_789",
                  "stripe_account": "acct_A",
                  "livemode": false,
                  "email": "ana@example.test",
                  "company_domain": null,
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {},
                  "churn_score": null,
                  "churn_level": null,
                  "flag": null,
                  "state": "none",
                  "plan": null,
                  "mrr_cents": 0,
                  "lifetime_value_cents": 0,
                  "currency": null,
                  "payment_method": null,
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": [
                    {
                      "kind": "email",
                      "provider": null,
                      "connection": null,
                      "value": "ana@example.test",
                      "source": "api",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "kind": "external",
                      "provider": null,
                      "connection": null,
                      "value": "u_789",
                      "source": "api",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "409": {
            "description": "external_id already used by another customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "unknown time zone",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "external_id": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "de",
                      "fr",
                      "it",
                      "pt"
                    ]
                  },
                  "country": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string",
                    "description": "State or province (US: TX)."
                  },
                  "timezone": {
                    "type": "string",
                    "nullable": true,
                    "description": "IANA zone. Wins over any guess; null goes back to the guess."
                  }
                }
              },
              "example": {
                "external_id": "u_789"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/customer_duplicates": {
      "get": {
        "summary": "List possible duplicate customers",
        "tags": [
          "Customers"
        ],
        "operationId": "listCustomerDuplicates",
        "description": "Two customers of this mode that may be the same person: they share an email, or one source named ids held by both. NoChurn never merges customers automatically; each proposal is also a To do (possible_duplicate), which a human dismisses or merges. suggested_keep: the customer to preselect (a live subscription, else billed by a tool, else the oldest).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "dismissed",
                "merged"
              ],
              "default": "open"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "open proposals of the current mode",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "customer_duplicate",
                      "id": "cdp_Mu1X3FMJ79pDfrKj",
                      "livemode": false,
                      "customer": "cst_jhuithY715hV64ga",
                      "other_customer": "cst_y4w5qHA2hRDq9mFK",
                      "people": [
                        {
                          "id": "cst_jhuithY715hV64ga",
                          "name": null,
                          "email": "ana@example.test"
                        },
                        {
                          "id": "cst_y4w5qHA2hRDq9mFK",
                          "name": null,
                          "email": "ana@example.test"
                        }
                      ],
                      "identifier": "email",
                      "reason": "share an email",
                      "status": "open",
                      "suggested_keep": "cst_y4w5qHA2hRDq9mFK",
                      "kept_customer": null,
                      "dismissed_at": null,
                      "merged_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicateList"
                }
              }
            }
          },
          "400": {
            "description": "unknown status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customer_duplicates/{id}/dismiss": {
      "post": {
        "summary": "Dismiss a possible duplicate (not the same person)",
        "tags": [
          "Customers"
        ],
        "operationId": "dismissCustomerDuplicate",
        "description": "Both customers stay as they are; the pair is never proposed again.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "dismissed",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_duplicate",
                  "id": "cdp_mmgZiHxMMvfqnn8G",
                  "livemode": false,
                  "customer": "cst_1Xto5g95GnymjNZJ",
                  "other_customer": "cst_gD495DbT1fr7KmA1",
                  "people": [
                    {
                      "id": "cst_1Xto5g95GnymjNZJ",
                      "name": null,
                      "email": "ana@example.test"
                    },
                    {
                      "id": "cst_gD495DbT1fr7KmA1",
                      "name": null,
                      "email": "bob@example.test"
                    }
                  ],
                  "identifier": "email",
                  "reason": "share an email",
                  "status": "dismissed",
                  "suggested_keep": null,
                  "kept_customer": null,
                  "dismissed_at": "2026-11-04T10:00:00.000Z",
                  "merged_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicate"
                }
              }
            }
          },
          "404": {
            "description": "unknown or in another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customer_duplicates/{id}/merge": {
      "post": {
        "summary": "Merge a possible duplicate (the same person)",
        "tags": [
          "Customers"
        ],
        "operationId": "mergeCustomerDuplicate",
        "description": "Irreversible. Everything the other customer has (subscriptions, invoices, payments, cards, flow runs, messages, cancel sessions, disputes, timeline, attributes, usage, identifiers) moves to `keep`, then the other customer is deleted. Its ids stay known: its cst_, Stripe and tool ids, external_id and email are identifiers of the kept customer, so webhooks and API calls that still name it reach the kept one. The kept customer's own values win (the other external_id becomes a previous id). Owner or admin. The same merge again answers the same proposal; another choice once merged, or a dismissed proposal, is a 409.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the same merge again: the same answer",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_duplicate",
                  "id": "cdp_GtWYvMRMiVrhHU3a",
                  "livemode": false,
                  "customer": "cst_2qdTqvEmV9hnpr9z",
                  "other_customer": "cst_inVEBWu2hf88HdGP",
                  "people": [
                    null,
                    {
                      "id": "cst_inVEBWu2hf88HdGP",
                      "name": null,
                      "email": "ana@example.test"
                    }
                  ],
                  "identifier": "email",
                  "reason": "share an email",
                  "status": "merged",
                  "suggested_keep": "cst_inVEBWu2hf88HdGP",
                  "kept_customer": "cst_inVEBWu2hf88HdGP",
                  "dismissed_at": null,
                  "merged_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerDuplicate"
                }
              }
            }
          },
          "400": {
            "description": "keep is not one of the two",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "in another account or the other mode",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already merged into the other customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "keep"
                ],
                "properties": {
                  "keep": {
                    "type": "string",
                    "description": "The customer to keep: one of the two (suggested_keep is the default to offer)."
                  }
                }
              },
              "example": {
                "keep": "cst_inVEBWu2hf88HdGP"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/offers/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Edit an offer",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "updateOffer",
        "description": "Published flow versions keep their frozen copy of the offer.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "edited",
            "content": {
              "application/json": {
                "example": {
                  "object": "offer",
                  "id": "off_vQW5kVg4HApSZfQX",
                  "kind": "discount",
                  "name": "25% for 3 months",
                  "params": {
                    "percent": 25,
                    "months": 3
                  },
                  "archived_at": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "params": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "name": "25% for 3 months",
                "params": {
                  "percent": 25,
                  "months": 3
                }
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Archive an offer",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "archiveOffer",
        "responses": {
          "200": {
            "description": "archived (published flows keep their copy)",
            "content": {
              "application/json": {
                "example": {
                  "object": "offer",
                  "id": "off_ZUAX1WJmcBxXHowk",
                  "kind": "discount",
                  "name": "30% for 3 months",
                  "params": {
                    "months": 3,
                    "percent": 30
                  },
                  "archived_at": "2026-11-04T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/cancel_sessions/{id}/replay": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "get": {
        "summary": "Replay a cancel session",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "replayCancelSession",
        "responses": {
          "200": {
            "description": "the timestamped steps",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_session_replay",
                  "id": "ses_AMW6ttCwGtJUgAYx",
                  "steps": [
                    {
                      "at": "2026-11-04T10:00:00Z",
                      "step": "reason"
                    }
                  ],
                  "reason": null,
                  "comment": null,
                  "feedback": null,
                  "outcome": null,
                  "cancel_status": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelSessionReplay"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/review_platforms/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Edit or disable a review page",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "updateReviewPlatform",
        "parameters": [],
        "responses": {
          "200": {
            "description": "edited",
            "content": {
              "application/json": {
                "example": {
                  "object": "review_platform",
                  "id": "rvp_WdXYG22G1uHGFB2V",
                  "kind": "g2",
                  "url": "https://g2.com/acme",
                  "status": "disabled",
                  "review_count": 42,
                  "rating": null,
                  "rules": {},
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ReviewPlatform"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "review_count": {
                    "type": "integer"
                  },
                  "rating": {
                    "type": "number"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "disabled"
                    ]
                  },
                  "rules": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "review_count": 42,
                "status": "disabled"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Remove a review page",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "deleteReviewPlatform",
        "description": "Deleted, or disabled when reviews were already requested on it.",
        "responses": {
          "204": {
            "description": "removed"
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/replies/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "get": {
        "summary": "One reply",
        "tags": [
          "Messages"
        ],
        "operationId": "getReply",
        "responses": {
          "200": {
            "description": "the reply",
            "content": {
              "application/json": {
                "example": {
                  "object": "message_reply",
                  "id": "rpl_QvzvaP4p8DDp6VTq",
                  "message": "msg_EBNrBPU9L5SuTsjj",
                  "customer": "cst_mz3wQqTbGGtZ36N6",
                  "customer_email": "ana@example.test",
                  "customer_name": null,
                  "flow_run": null,
                  "flow": null,
                  "from": "ana@example.test",
                  "subject": "Re: Hi",
                  "text": "Thanks?",
                  "category": "question",
                  "summary": null,
                  "needs_human": true,
                  "status": "open",
                  "handled_at": null,
                  "answer_message": null,
                  "received_at": "2026-11-04T10:00:00Z",
                  "livemode": false
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageReply"
                }
              }
            }
          },
          "404": {
            "description": "unknown",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/stats": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "description": "flw_… or the kind (prevention, recovery, cancel, winback).",
          "required": true
        },
        {
          "name": "period",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string",
            "enum": [
              "month",
              "30d",
              "90d",
              "12m"
            ],
            "default": "month"
          }
        }
      ],
      "get": {
        "summary": "KPIs, funnel and holdout of a flow",
        "tags": [
          "Flows"
        ],
        "operationId": "getFlowStats",
        "responses": {
          "200": {
            "description": "the recovery flow this month",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow_stats",
                  "flow": "flw_JgjjWhZqyvzdjbiw",
                  "kind": "recovery",
                  "livemode": false,
                  "period": {
                    "key": "month",
                    "from": "2026-11-01T00:00:00+01:00",
                    "to": "2026-11-04T11:00:00+01:00"
                  },
                  "kpis": [
                    {
                      "key": "revenue_recovered",
                      "label": "Revenue recovered",
                      "value": 4900,
                      "unit": "money",
                      "sample_size": 1,
                      "pending": 0,
                      "currency": "eur",
                      "breakdown": [
                        {
                          "currency": "eur",
                          "amount_cents": 4900,
                          "converted_cents": 4900,
                          "unconverted_cents": 0
                        }
                      ]
                    },
                    {
                      "key": "recovery_rate",
                      "label": "Recovery rate",
                      "value": 1,
                      "unit": "rate",
                      "sample_size": 1
                    },
                    {
                      "key": "in_progress",
                      "label": "In progress",
                      "value": 1,
                      "unit": "count",
                      "sample_size": 1
                    },
                    {
                      "key": "median_days_to_recover",
                      "label": "Median time to recover",
                      "value": 0,
                      "unit": "days",
                      "sample_size": 1
                    }
                  ],
                  "funnel": [
                    {
                      "key": "payment_failed",
                      "label": "Payment failed",
                      "count": 2,
                      "share": 1
                    },
                    {
                      "key": "smart_retry",
                      "label": "Smart retry",
                      "count": 1,
                      "share": 0.5
                    },
                    {
                      "key": "ai_email",
                      "label": "AI email",
                      "count": 0,
                      "share": 0
                    },
                    {
                      "key": "card_page",
                      "label": "Card update page",
                      "count": 0,
                      "share": 0
                    },
                    {
                      "key": "recovered",
                      "label": "Recovered",
                      "count": 1,
                      "share": 0.5
                    }
                  ],
                  "holdout": {
                    "percent": 0,
                    "runs": 0,
                    "natural_rate": null,
                    "treated_rate": 0,
                    "lift": null,
                    "sample_size": 2
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowStats"
                }
              }
            }
          },
          "400": {
            "description": "unknown period",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/usage_events": {
      "post": {
        "summary": "Track a usage event",
        "tags": [
          "Data & AI"
        ],
        "operationId": "createUsageEvent",
        "description": "Rolled up each night into last_active_at, events_30d and <key event>_30d (custom attributes). Deduplicated on idempotency_key. Never sent to the AI.",
        "parameters": [],
        "responses": {
          "202": {
            "description": "accepted",
            "content": {
              "application/json": {
                "example": {
                  "object": "usage_event_batch",
                  "accepted": 1,
                  "inserted": 1,
                  "duplicates": 0,
                  "errors": []
                },
                "schema": {
                  "$ref": "#/components/schemas/UsageEventBatch"
                }
              }
            }
          },
          "400": {
            "description": "unknown event name format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown customer (its own code and fix)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer",
                  "name"
                ],
                "properties": {
                  "customer": {
                    "type": "string",
                    "description": "cst_…, cus_… or your own id."
                  },
                  "name": {
                    "type": "string",
                    "example": "export_used"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "properties": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Any JSON object (50 keys kept)."
                  },
                  "idempotency_key": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "customer": "u_1",
                "name": "export_used",
                "idempotency_key": "e1"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/usage_events/batch": {
      "post": {
        "summary": "Track up to 1,000 events",
        "tags": [
          "Data & AI"
        ],
        "operationId": "createUsageEventBatch",
        "parameters": [],
        "responses": {
          "202": {
            "description": "errors are reported per event",
            "content": {
              "application/json": {
                "example": {
                  "object": "usage_event_batch",
                  "accepted": 1,
                  "inserted": 1,
                  "duplicates": 0,
                  "errors": [
                    {
                      "index": 1,
                      "code": "customer_not_found",
                      "message": "No customer matches \"u_404\" in test mode."
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/UsageEventBatch"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "events"
                ],
                "properties": {
                  "events": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "type": "object",
                      "required": [
                        "customer",
                        "name"
                      ],
                      "properties": {
                        "customer": {
                          "type": "string",
                          "description": "cst_…, cus_… or your own id."
                        },
                        "name": {
                          "type": "string"
                        },
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "properties": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "Any JSON object (50 keys kept)."
                        },
                        "idempotency_key": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "events": [
                  {
                    "customer": "u_1",
                    "name": "login"
                  },
                  {
                    "customer": "u_404",
                    "name": "login"
                  }
                ]
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/insights": {
      "get": {
        "summary": "Churn drivers and AI insights",
        "tags": [
          "Data & AI"
        ],
        "operationId": "getInsights",
        "description": "The last statistics run (relative risk, Wilson interval, Benjamini-Hochberg q-value per bucket; sample_ok false under 30 customers or 5 departures) and the last validated AI insight.",
        "responses": {
          "200": {
            "description": "nothing computed yet",
            "content": {
              "application/json": {
                "example": {
                  "object": "insights",
                  "run": null,
                  "insight": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Insights"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/insights/refresh": {
      "post": {
        "summary": "Recompute now (once an hour)",
        "tags": [
          "Data & AI"
        ],
        "operationId": "refreshInsights",
        "responses": {
          "202": {
            "description": "queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "insights_refresh",
                  "queued": true
                },
                "schema": {
                  "$ref": "#/components/schemas/InsightsRefresh"
                }
              }
            }
          },
          "429": {
            "description": "already refreshed this hour",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/campaigns": {
      "parameters": [
        {
          "name": "group_by",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string",
            "enum": [
              "utm_campaign",
              "utm_source",
              "utm_medium"
            ],
            "default": "utm_campaign"
          }
        },
        {
          "name": "period",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string",
            "enum": [
              "90d",
              "12m"
            ],
            "default": "90d"
          }
        }
      ],
      "get": {
        "summary": "Churn by acquisition campaign",
        "tags": [
          "Data & AI"
        ],
        "operationId": "getCampaigns",
        "description": "From utm_* attributes of customers who signed up in the period. Correlation, not causation.",
        "responses": {
          "200": {
            "description": "rows with their sample size",
            "content": {
              "application/json": {
                "example": {
                  "object": "campaign_stats",
                  "group_by": "utm_campaign",
                  "period": "90d",
                  "customers": 1,
                  "churned": 0,
                  "churn_rate": 0,
                  "tracked": 1,
                  "best": null,
                  "worst": null,
                  "min_sample": 30,
                  "rows": [
                    {
                      "value": "black_friday",
                      "customers": 1,
                      "churned": 0,
                      "churn_rate": 0,
                      "relative_risk": null,
                      "avg_ltv_cents": 0,
                      "avg_score": null,
                      "mrr_lost_cents": 0,
                      "sample_ok": false
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/CampaignStats"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/disputes": {
      "get": {
        "summary": "Disputes",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "listDisputes",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma separated statuses."
          },
          {
            "name": "provider",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "due_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "opened_at",
                "evidence_due_by"
              ],
              "default": "opened_at"
            },
            "description": "opened_at: newest first. evidence_due_by: soonest deadline first, disputes without a deadline last."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "newest first, without card data",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "dsp_2F9vssKp8BhfWwe9",
                      "object": "dispute",
                      "livemode": false,
                      "provider": "stripe",
                      "billing_connection": "scn_VDpCSHRTXyhtofFq",
                      "external_id": "dp_1",
                      "charge": "ch_1",
                      "customer": "cst_5B4srTLYwe9m6DyR",
                      "customer_name": "Lena",
                      "customer_email": "customer1@acme-corp.dev",
                      "customer_plan": "Pro",
                      "amount_cents": 4900,
                      "currency": "eur",
                      "reason": "subscription_canceled",
                      "status": "needs_response",
                      "evidence_due_by": "2026-11-09T10:00:00Z",
                      "opened_at": "2026-11-04T10:00:00Z",
                      "outcome_at": null,
                      "submitted_at": null,
                      "submitted_by": null,
                      "provider_submitted": false,
                      "read_only": false,
                      "capability": "evidence_api",
                      "evidence_count": 2,
                      "draft_status": "draft",
                      "win_score": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/disputes/summary": {
      "get": {
        "summary": "Counts of the Disputes tab (each with its sample size)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "disputesSummary",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 90
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the saved amount counts only disputes won",
            "content": {
              "application/json": {
                "example": {
                  "object": "disputes_summary",
                  "livemode": false,
                  "days": 90,
                  "open": 1,
                  "due_within_72h": 0,
                  "next_due_at": "2026-11-09T10:00:00Z",
                  "drafts_ready": 1,
                  "open_amount": [
                    {
                      "currency": "eur",
                      "amount_cents": 4900
                    }
                  ],
                  "won": 0,
                  "lost": 0,
                  "decided": 0,
                  "win_rate": null,
                  "win_score_accuracy": {
                    "decided": 0,
                    "right": 0,
                    "rate": null
                  },
                  "won_amount": [],
                  "fraud_warnings_open": 0,
                  "fraud_warnings_refunded": 0
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputesSummary"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/disputes/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "get": {
        "summary": "A dispute with its evidence and draft",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "getDispute",
        "responses": {
          "200": {
            "description": "found",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_nu5bHBnJb3n4KEEQ",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_37LhkkzpDAwwa5cU",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_JzQ8sUdXtXqzqq7x",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "draft",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_iqCLHm6bhLAX1ZDa",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_kgW2ZRLXZHVnuFmk",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_GoqrRZdQYbEjCerZ",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_iqCLHm6bhLAX1ZDa",
                      "dev_kgW2ZRLXZHVnuFmk"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": null,
                    "reason": "No evidence from a record. Set the cost of contesting in Chargebacks › Settings to compare it with the amount.",
                    "evidence_count": 0,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": null,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "404": {
            "description": "another account's dispute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/disputes/{id}/evidence": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Include, exclude, reorder or add evidence",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "updateDisputeEvidence",
        "parameters": [],
        "responses": {
          "200": {
            "description": "edited",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_Rj5wjxQEaNy9JPW1",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_QKuRXU8hJQqAHwRc",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_iHbcRo3tbNDxfwMv",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 3,
                  "draft_status": "draft",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_sfrkQKYjndBpn7sB",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": false,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_DV9iYbojhjifRhC7",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    },
                    {
                      "id": "dev_qdxtS4tUW5xDEPZ4",
                      "object": "dispute_evidence_item",
                      "kind": "other",
                      "title": "Terms accepted at signup",
                      "content": "v3, 2026-01-04",
                      "source_type": "manual",
                      "source_id": "54c0dcfe-ff8a-42ce-a4bb-720a027198e9",
                      "occurred_at": null,
                      "included": true,
                      "position": 3,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_E1iWTCka5FaM988J",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_sfrkQKYjndBpn7sB",
                      "dev_DV9iYbojhjifRhC7"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": "contest",
                    "reason": "1 piece of evidence from NoChurn's records.",
                    "evidence_count": 1,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": null,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "id"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "included": {
                          "type": "boolean"
                        },
                        "position": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "add": {
                    "type": "object",
                    "required": [
                      "title"
                    ],
                    "properties": {
                      "title": {
                        "type": "string"
                      },
                      "content": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "items": [
                  {
                    "id": "dev_sfrkQKYjndBpn7sB",
                    "included": false
                  }
                ],
                "add": {
                  "title": "Terms accepted at signup",
                  "content": "v3, 2026-01-04"
                }
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/evidence_files": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Attach a file as evidence (admin+)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "addDisputeEvidenceFile",
        "description": "PDF, JPEG or PNG, 5 MB at most per file; the type is read from the file's bytes, not its name. For a Stripe dispute, one file per evidence field (the field of its kind, then uncategorized_file) and 4.5 MB of included files in all. Kept by NoChurn until the reply is sent, then given to the provider (Stripe Files API, Razorpay documents); a provider that takes no files (Braintree) gets the file named in the reply. Refused once the reply is sent or for a dispute the provider handles.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "attached, with dispute.evidence_file_added",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_Na6rvG7ku8MtRaDv",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_mLiVFHx89vq9XnXZ",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_64GTYRa6AoYRm1H7",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 3,
                  "draft_status": "draft",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_RfiKG9mqpf14mwbe",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_AkhDCxNfD6Kt6Gd9",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    },
                    {
                      "id": "dev_jTobuAp3akY663Wa",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Terms accepted at signup",
                      "content": "File attached by the merchant: terms.pdf (PDF, 51 Bytes).",
                      "source_type": "file",
                      "source_id": "9df4060a-9560-4835-9636-af43eaf834e7",
                      "occurred_at": "2026-11-04T10:00:00Z",
                      "included": true,
                      "position": 3,
                      "file": {
                        "filename": "terms.pdf",
                        "content_type": "application/pdf",
                        "byte_size": 51,
                        "sent_to_provider": false
                      }
                    }
                  ],
                  "draft": {
                    "id": "ddr_VxjQntJkeR1w9xhb",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_RfiKG9mqpf14mwbe",
                      "dev_AkhDCxNfD6Kt6Gd9"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": "contest",
                    "reason": "1 piece of evidence from NoChurn's records.",
                    "evidence_count": 1,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": null,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "409": {
            "description": "the provider handles this dispute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "over 5 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "not a PDF, JPEG or PNG (read from the bytes)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PDF, JPEG or PNG, at most 5 MB."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "communication",
                      "policy",
                      "usage",
                      "reminder",
                      "cancel_history",
                      "refund_history",
                      "receipt",
                      "other"
                    ],
                    "default": "other",
                    "description": "What the file proves; picks its Stripe evidence field."
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Defaults to the file name."
                  }
                }
              },
              "example": {
                "file": "terms.pdf",
                "kind": "policy",
                "title": "Terms accepted at signup"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/disputes/{id}/evidence_files/{file_id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        },
        {
          "name": "file_id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "description": "The evidence item (dev_…) that holds the file.",
          "required": true
        }
      ],
      "delete": {
        "summary": "Remove an attached file before sending (admin+)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "removeDisputeEvidenceFile",
        "responses": {
          "200": {
            "description": "removed, with dispute.evidence_file_removed",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_YLweuRk1thhtFAYb",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_mCEQdXjjESrDrJDG",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_FtWgoBbNnircx8KD",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "draft",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_4wYdXTX8SmoX9sxY",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_e4s6jyuCNDEvXSnz",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_hrCWnUdvgHduoCqh",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_4wYdXTX8SmoX9sxY",
                      "dev_e4s6jyuCNDEvXSnz"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": null,
                    "reason": "No evidence from a record. Set the cost of contesting in Chargebacks › Settings to compare it with the amount.",
                    "evidence_count": 0,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": null,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "404": {
            "description": "gathered evidence (exclude it instead) or another dispute's file",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "the reply was already sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/disputes/{id}/draft": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Draft the reply again (AI, from the included evidence)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "regenerateDisputeDraft",
        "responses": {
          "201": {
            "description": "drafted",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_HtxpkYjWAKTfm2Up",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_J9fwTTz5CCnMQ9aH",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_tTkMw8fApKsvgKJS",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "draft",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_btvZe1j2d5g3NpzY",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_QTqAuzsLxD1tJMvd",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_TZhtPxA84uU2PQ3i",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_btvZe1j2d5g3NpzY",
                      "dev_QTqAuzsLxD1tJMvd"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": null,
                    "reason": "No evidence from a record. Set the cost of contesting in Chargebacks › Settings to compare it with the amount.",
                    "evidence_count": 0,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": null,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Edit or approve the reply",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "updateDisputeDraft",
        "parameters": [],
        "responses": {
          "200": {
            "description": "edited, with dispute.draft_edited",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_qsKJ2P9jLqWDH6Ef",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_ZZJu5H56QFUahsJ1",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_ugS68ZNSH6gn8HwH",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "approved",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_SUnk9iZDSZbDHAQh",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_tiRpmG1reTLXsQag",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_iN69iJm2qEJVTAcB",
                    "object": "dispute_draft",
                    "text": "We contest: the customer never asked to cancel.",
                    "cited_item_ids": [
                      "dev_SUnk9iZDSZbDHAQh",
                      "dev_tiRpmG1reTLXsQag"
                    ],
                    "model": "template",
                    "status": "approved",
                    "edited_by": "key_V7qug9n5dKhA1X9u",
                    "edited_at": "2026-11-04T10:00:00Z",
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": null,
                    "reason": "No evidence from a record. Set the cost of contesting in Chargebacks › Settings to compare it with the amount.",
                    "evidence_count": 0,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": null,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "approved"
                    ]
                  }
                }
              },
              "example": {
                "text": "We contest: the customer never asked to cancel.",
                "status": "approved"
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/submit": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Send the reply to the provider (admin+, final)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "submitDispute",
        "parameters": [],
        "responses": {
          "200": {
            "description": "sent once",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_QzSx4QbyyTkisnRu",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_DZQ97JHJBDsX7jrm",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_LLSMeaxBDkN75sB1",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "under_review",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": "2026-11-04T10:00:00Z",
                  "submitted_by": "key_Gb3rYm1g8cgZ6EtX",
                  "provider_submitted": false,
                  "read_only": true,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "sent",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_886VuUUzD8FfhMYo",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_vo1o3ad11iPAmsYJ",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_Ryx7fL7364Rn2p6h",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_886VuUUzD8FfhMYo",
                      "dev_vo1o3ad11iPAmsYJ"
                    ],
                    "model": "template",
                    "status": "sent",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": null
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "400": {
            "description": "no explicit confirmation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirm"
                ],
                "properties": {
                  "confirm": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  },
                  "text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "confirm": true
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/accept": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Accept the dispute (admin+, final)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "acceptDispute",
        "parameters": [],
        "responses": {
          "200": {
            "description": "accepted",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_8BSADXr3AJYpLChN",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_6SJXLf7oAZC72G86",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_AQJjCENwQCV4zoni",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "accepted",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": "2026-11-04T10:00:00Z",
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": true,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "draft",
                  "win_score": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_zeBXp3xgZYonNaAr",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_cCLChQGq6RWCBhqN",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_qEE9rTYVtmPtMSid",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_zeBXp3xgZYonNaAr",
                      "dev_cCLChQGq6RWCBhqN"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": null
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "409": {
            "description": "the provider handles this dispute (merchant of record)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirm"
                ],
                "properties": {
                  "confirm": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                }
              },
              "example": {
                "confirm": true
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/win_score": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Set the chance of winning yourself, or give it back to the AI (admin+)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "setDisputeWinScore",
        "parameters": [],
        "responses": {
          "200": {
            "description": "set, with dispute.win_score_set",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsp_LAPfbdgzPUevBEks",
                  "object": "dispute",
                  "livemode": false,
                  "provider": "stripe",
                  "billing_connection": "scn_ZhJbRPTgnePzxjVs",
                  "external_id": "dp_1",
                  "charge": "ch_1",
                  "customer": "cst_jG4aKZ2xDye6LEtD",
                  "customer_name": "Lena",
                  "customer_email": "customer1@acme-corp.dev",
                  "customer_plan": "Pro",
                  "amount_cents": 4900,
                  "currency": "eur",
                  "reason": "subscription_canceled",
                  "status": "needs_response",
                  "evidence_due_by": "2026-11-09T10:00:00Z",
                  "opened_at": "2026-11-04T10:00:00Z",
                  "outcome_at": null,
                  "submitted_at": null,
                  "submitted_by": null,
                  "provider_submitted": false,
                  "read_only": false,
                  "capability": "evidence_api",
                  "evidence_count": 2,
                  "draft_status": "draft",
                  "win_score": {
                    "score": 65,
                    "source": "manual",
                    "confidence": null,
                    "factors": [],
                    "evidence_count": null,
                    "model": null,
                    "set_by": "key_KtYf839pxVaXiWsn",
                    "computed_at": "2026-11-04T10:00:00Z"
                  },
                  "created_at": "2026-11-04T10:00:00Z",
                  "evidence": [
                    {
                      "id": "dev_JdC44XCLiiz2kLxu",
                      "object": "dispute_evidence_item",
                      "kind": "cancel_history",
                      "title": "No cancellation request",
                      "content": "The customer never opened the cancellation flow before the dispute.",
                      "source_type": "nochurn",
                      "source_id": "cancel_history",
                      "occurred_at": null,
                      "included": true,
                      "position": 1,
                      "file": null
                    },
                    {
                      "id": "dev_MPZaQk8n7KM3cABs",
                      "object": "dispute_evidence_item",
                      "kind": "policy",
                      "title": "Cancellation policy",
                      "content": "Cancellation is available at any time in the app.",
                      "source_type": "nochurn",
                      "source_id": "policy",
                      "occurred_at": null,
                      "included": true,
                      "position": 2,
                      "file": null
                    }
                  ],
                  "draft": {
                    "id": "ddr_V9zGUkjGoVRGn4zG",
                    "object": "dispute_draft",
                    "text": "We contest this subscription canceled dispute.\n\nEvidence:\n- No cancellation request: The customer never opened the cancellation flow before the dispute.\n- Cancellation policy: Cancellation is available at any time in the app.",
                    "cited_item_ids": [
                      "dev_JdC44XCLiiz2kLxu",
                      "dev_MPZaQk8n7KM3cABs"
                    ],
                    "model": "template",
                    "status": "draft",
                    "edited_by": null,
                    "edited_at": null,
                    "created_at": "2026-11-04T10:00:00Z"
                  },
                  "charge_details": {
                    "paid_at": "2026-11-03T10:00:00Z",
                    "plan": "Pro",
                    "invoice": null,
                    "period_end": null
                  },
                  "recommendation": {
                    "suggestion": null,
                    "reason": "No evidence from a record. Set the cost of contesting in Chargebacks › Settings to compare it with the amount.",
                    "evidence_count": 0,
                    "amount_cents": 4900,
                    "contest_cost_cents": null,
                    "currency": "eur",
                    "win_score": 65,
                    "expected_gain_cents": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "400": {
            "description": "not an integer from 0 to 100",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "score"
                ],
                "properties": {
                  "score": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "nullable": true
                  }
                }
              },
              "example": {
                "score": 65
              }
            }
          }
        }
      }
    },
    "/v1/fraud_warnings": {
      "get": {
        "summary": "Early fraud warnings",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "listFraudWarnings",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "none",
                "refunded",
                "dismissed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "listed",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "efw_UV9yBsfrkR2nKpAR",
                      "object": "fraud_warning",
                      "livemode": false,
                      "provider": "stripe",
                      "external_id": "issfr_1",
                      "charge": "ch_9",
                      "customer": null,
                      "customer_name": null,
                      "customer_email": null,
                      "customer_plan": null,
                      "amount_cents": null,
                      "currency": null,
                      "fraud_type": "misc",
                      "actionable": true,
                      "action": "none",
                      "acted_by": null,
                      "acted_at": null,
                      "note": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/FraudWarningList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/fraud_warnings/{id}/refund": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Refund before the dispute (admin+, final)",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "refundFraudWarning",
        "parameters": [],
        "responses": {
          "200": {
            "description": "refunded once",
            "content": {
              "application/json": {
                "example": {
                  "id": "efw_K5887AXzbU8YgLAM",
                  "object": "fraud_warning",
                  "livemode": false,
                  "provider": "stripe",
                  "external_id": "issfr_1",
                  "charge": "ch_9",
                  "customer": null,
                  "customer_name": null,
                  "customer_email": null,
                  "customer_plan": null,
                  "amount_cents": null,
                  "currency": null,
                  "fraud_type": "misc",
                  "actionable": true,
                  "action": "refunded",
                  "acted_by": "key_zA9EmYgpvf29Cd1D",
                  "acted_at": "2026-11-04T10:00:00Z",
                  "note": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/FraudWarning"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirm"
                ],
                "properties": {
                  "confirm": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                }
              },
              "example": {
                "confirm": true
              }
            }
          }
        }
      }
    },
    "/v1/fraud_warnings/{id}/dismiss": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Keep the payment",
        "tags": [
          "Chargebacks"
        ],
        "operationId": "dismissFraudWarning",
        "parameters": [],
        "responses": {
          "200": {
            "description": "dismissed",
            "content": {
              "application/json": {
                "example": {
                  "id": "efw_gw2bmQewwphGBtAv",
                  "object": "fraud_warning",
                  "livemode": false,
                  "provider": "stripe",
                  "external_id": "issfr_1",
                  "charge": "ch_9",
                  "customer": null,
                  "customer_name": null,
                  "customer_email": null,
                  "customer_plan": null,
                  "amount_cents": null,
                  "currency": null,
                  "fraud_type": "misc",
                  "actionable": true,
                  "action": "dismissed",
                  "acted_by": "key_TaFFSqDwmBtFwbL3",
                  "acted_at": "2026-11-04T10:00:00Z",
                  "note": "Known customer",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/FraudWarning"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "note": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "note": "Known customer"
              }
            }
          }
        }
      }
    },
    "/v1/flows/{id}/email_preview": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "description": "flw_… or the kind of the default flow",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Preview an email of a flow",
        "tags": [
          "Flows"
        ],
        "operationId": "previewFlowEmail",
        "description": "Renders one email of the flow's draft for one language, from your text (saved, or `draft_content` being typed), the AI or NoChurn's template, with an example customer (fictional data) or one of your customers (`customer_id`, in the key's mode). Sends nothing, stores nothing. `source: ai` shows the last email the AI wrote for this step, or the template; `generate: true` asks the AI for a sample (20 a day). A restricted key needs flows:read.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "the email, rendered",
            "content": {
              "application/json": {
                "example": {
                  "object": "email_preview",
                  "step": "email_1",
                  "label": "Email 1 · day 0",
                  "language": "fr",
                  "source": "template",
                  "subject": "Votre paiement n'est pas passé",
                  "text": "Bonjour Alex,\n\nVotre dernier paiement de €49.00 pour Acme n'est pas passé. Cela arrive souvent quand une carte a expiré ou a été remplacée.\n\nVous pouvez mettre à jour votre moyen de paiement en une minute : https://pay.nochurn.sh/example\n\nMerci,\nAcme",
                  "html": "<p>Bonjour Alex,</p><p>Votre dernier paiement de €49.00 pour Acme n&#39;est pas passé. Cela arrive souvent quand une carte a expiré ou a été remplacée.</p><p>Vous pouvez mettre à jour votre moyen de paiement en une minute : <a href=\"https://pay.nochurn.sh/example\">https://pay.nochurn.sh/example</a></p><p>Merci,<br>Acme</p>",
                  "ai_generated": false,
                  "review": {
                    "state": "auto_approved",
                    "issues": []
                  },
                  "warnings": [
                    "Links are examples: each customer gets their own link"
                  ],
                  "customer": {
                    "id": null,
                    "name": "Example customer",
                    "example": true
                  },
                  "sample": true,
                  "template": {
                    "subject": "Votre paiement n'est pas passé",
                    "body": "Bonjour {{first_name}},\n\nVotre dernier paiement de {{amount}} pour {{company}} n'est pas passé. Cela arrive souvent quand une carte a expiré ou a été remplacée.\n\nVous pouvez mettre à jour votre moyen de paiement en une minute : {{card_link}}\n\nMerci,\n{{sender_name}}"
                  },
                  "custom": null,
                  "placeholders": [
                    "first_name",
                    "amount",
                    "company",
                    "card_link",
                    "sender_name"
                  ],
                  "required_placeholders": [
                    "card_link"
                  ],
                  "languages": [
                    "en",
                    "fr",
                    "de",
                    "it",
                    "pt"
                  ],
                  "sources": [
                    "custom",
                    "ai",
                    "template"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/EmailPreview"
                }
              }
            }
          },
          "400": {
            "description": "unknown step",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "step"
                ],
                "properties": {
                  "step": {
                    "type": "string",
                    "example": "email_1",
                    "description": "email_1…, card_removed, card_expiring, renewal_reminder, d30, d60, d90, review_follow_up"
                  },
                  "language": {
                    "type": "string",
                    "example": "fr",
                    "description": "Default: the flow's first language."
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "custom",
                      "ai",
                      "template"
                    ],
                    "description": "Default: what the customer would get."
                  },
                  "draft_content": {
                    "type": "object",
                    "required": [
                      "subject",
                      "body"
                    ],
                    "properties": {
                      "subject": {
                        "type": "string"
                      },
                      "body": {
                        "type": "string"
                      }
                    },
                    "description": "Your text being typed, not saved yet."
                  },
                  "customer_id": {
                    "type": "string",
                    "description": "cst_…, cus_… or your id: fill the placeholders with this customer's data."
                  },
                  "generate": {
                    "type": "boolean",
                    "description": "With source ai: ask the AI for a sample now."
                  }
                }
              },
              "example": {
                "step": "email_1",
                "language": "fr",
                "source": "template"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/inapp/resume": {
      "post": {
        "summary": "Resume the paused subscription (in-app \"Resume now\")",
        "tags": [
          "In-app"
        ],
        "operationId": "resumeInappSubscription",
        "description": "From the paused wall or banner (DECISIONS 87): publishable key + customer token (its `subscription` claim names the subscription when there are several), or a secret key and ?customer=. Resumes the paused subscription at the provider (Stripe with an Idempotency-Key), writes a subscription.resumed event, and returns the new state (access full at once). 409 nothing_to_resume when nothing is paused (a second click included). Rate-limited like /v1/inapp/dismiss.",
        "parameters": [
          {
            "name": "X-NoChurn-Customer-Token",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "resumed: access full again, an event, the access webhook restored",
            "content": {
              "application/json": {
                "example": {
                  "object": "inapp_state",
                  "customer": "cst_33a3ErfXGkaBhxVd",
                  "failing_since": null,
                  "locale": "en",
                  "texts": null,
                  "theme": null,
                  "access": "full",
                  "access_reason": null,
                  "banner": {
                    "show": false
                  },
                  "wall": {
                    "show": false
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/InappState"
                }
              }
            }
          },
          "401": {
            "description": "a token from another account never resumes this customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "nothing paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/events": {
      "get": {
        "summary": "List events, newest first",
        "tags": [
          "Events"
        ],
        "operationId": "listEvents",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by event type, e.g. api_key.created"
          },
          {
            "name": "subject",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by subject id, e.g. cst_… for a customer timeline"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Cursor: the next_cursor of the previous page."
          }
        ],
        "responses": {
          "200": {
            "description": "events of the account, newest first",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "event",
                      "id": "nevt_wr9AZ4FaA7NVz3Z7",
                      "type": "api_key.created",
                      "subject": {
                        "type": "api_key",
                        "id": "key_xBrmAzbrotsgj2iT"
                      },
                      "reason": "test",
                      "actor": {
                        "type": "api_key",
                        "id": "key_xBrmAzbrotsgj2iT",
                        "name": null
                      },
                      "livemode": false,
                      "data": {},
                      "created_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "event",
                      "id": "nevt_rRmSZmTKnHXN5bcN",
                      "type": "account.created",
                      "subject": null,
                      "reason": null,
                      "actor": {
                        "type": "system",
                        "id": null,
                        "name": null
                      },
                      "livemode": false,
                      "data": {},
                      "created_at": "2026-11-02T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                }
              }
            }
          },
          "400": {
            "description": "invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flow_notifications": {
      "get": {
        "summary": "Slack messages and Intercom notes sent when a flow acts",
        "tags": [
          "Flows"
        ],
        "operationId": "getFlowNotifications",
        "description": "In the current mode. Slack: a message in a channel. Intercom: an internal note on the customer's contact (found by email), never a message to the customer. Each event is sent once per destination, retried with backoff and logged (Logs › webhook).",
        "responses": {
          "200": {
            "description": "the settings",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow_notifications",
                  "livemode": false,
                  "events": [
                    {
                      "type": "recovery.opened",
                      "flow": "recovery"
                    },
                    {
                      "type": "recovery.recovered",
                      "flow": "recovery"
                    },
                    {
                      "type": "recovery.lost",
                      "flow": "recovery"
                    },
                    {
                      "type": "cancel_session.offer_accepted",
                      "flow": "cancel"
                    },
                    {
                      "type": "cancel_session.completed",
                      "flow": "cancel"
                    },
                    {
                      "type": "customer.flagged",
                      "flow": "prevention"
                    },
                    {
                      "type": "winback.reactivated",
                      "flow": "winback"
                    }
                  ],
                  "slack": {
                    "kind": "slack",
                    "connected": true,
                    "integration_status": "active",
                    "workspace": "Globex",
                    "events": [],
                    "channel": "#alerts",
                    "notes_access": null,
                    "last_delivery_at": null,
                    "last_error": null
                  },
                  "intercom": {
                    "kind": "intercom_note",
                    "connected": false,
                    "integration_status": null,
                    "workspace": null,
                    "events": [],
                    "channel": null,
                    "notes_access": null,
                    "last_delivery_at": null,
                    "last_error": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowNotifications"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flow_notifications/{kind}": {
      "parameters": [
        {
          "name": "kind",
          "in": "path",
          "enum": [
            "slack",
            "intercom_note"
          ],
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "summary": "Choose the events sent to Slack (and its channel) or added as Intercom notes",
        "tags": [
          "Flows"
        ],
        "operationId": "updateFlowNotifications",
        "description": "Admins only. `events: []` turns the destination off. The integration must be connected to pick events.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "saved",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow_notifications",
                  "livemode": false,
                  "events": [
                    {
                      "type": "recovery.opened",
                      "flow": "recovery"
                    },
                    {
                      "type": "recovery.recovered",
                      "flow": "recovery"
                    },
                    {
                      "type": "recovery.lost",
                      "flow": "recovery"
                    },
                    {
                      "type": "cancel_session.offer_accepted",
                      "flow": "cancel"
                    },
                    {
                      "type": "cancel_session.completed",
                      "flow": "cancel"
                    },
                    {
                      "type": "customer.flagged",
                      "flow": "prevention"
                    },
                    {
                      "type": "winback.reactivated",
                      "flow": "winback"
                    }
                  ],
                  "slack": {
                    "kind": "slack",
                    "connected": true,
                    "integration_status": "active",
                    "workspace": "Globex",
                    "events": [
                      "cancel_session.completed",
                      "recovery.recovered"
                    ],
                    "channel": "#retention",
                    "notes_access": null,
                    "last_delivery_at": null,
                    "last_error": null
                  },
                  "intercom": {
                    "kind": "intercom_note",
                    "connected": false,
                    "integration_status": null,
                    "workspace": null,
                    "events": [],
                    "channel": null,
                    "notes_access": null,
                    "last_delivery_at": null,
                    "last_error": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowNotifications"
                }
              }
            }
          },
          "409": {
            "description": "not connected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "an event that is not notified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "recovery.opened",
                        "recovery.recovered",
                        "recovery.lost",
                        "cancel_session.offer_accepted",
                        "cancel_session.completed",
                        "customer.flagged",
                        "winback.reactivated"
                      ]
                    }
                  },
                  "channel": {
                    "type": "string",
                    "description": "Slack only: #channel or a channel id. A private channel needs the NoChurn app invited."
                  }
                }
              },
              "example": {
                "events": [
                  "cancel_session.completed",
                  "recovery.recovered"
                ],
                "channel": "#retention"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/flow_notifications/{kind}/test": {
      "parameters": [
        {
          "name": "kind",
          "in": "path",
          "enum": [
            "slack"
          ],
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Send a test message to the Slack channel now",
        "tags": [
          "Flows"
        ],
        "operationId": "testFlowNotifications",
        "responses": {
          "201": {
            "description": "posted",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_delivery",
                  "id": "whd_4z6TAYS8bp3otSoZ",
                  "livemode": false,
                  "endpoint": "whk_Px7rFYqisAcTaDdj",
                  "destination": "slack",
                  "event": null,
                  "type": "test",
                  "status": "delivered",
                  "attempts": 1,
                  "next_attempt_at": null,
                  "response_status": 200,
                  "response_ms": null,
                  "error": null,
                  "delivered_at": "2026-11-04T10:00:00Z",
                  "failed_at": null,
                  "replay_of": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "payload": {
                    "id": "nevt_test_f6a8e4b5b4ce",
                    "data": {},
                    "type": "test",
                    "object": "event",
                    "created": 1793786400,
                    "livemode": false
                  },
                  "response_excerpt": "1.2"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows": {
      "get": {
        "summary": "List the 4 flows",
        "tags": [
          "Flows"
        ],
        "operationId": "listFlows",
        "responses": {
          "200": {
            "description": "prevention, recovery, cancel and win-back",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "flow",
                      "id": "flw_1K3ms53XtrQohYHb",
                      "kind": "prevention",
                      "key": "default",
                      "name": "Prevention flow",
                      "status": "draft",
                      "holdout_percent": 5,
                      "test_version": null,
                      "live_version": null,
                      "unpublished_changes": [],
                      "mode_status": {
                        "livemode": false,
                        "state": "draft",
                        "version": null,
                        "published_at": null,
                        "unpublished_changes": []
                      },
                      "draft_updated_at": "2026-11-04T10:00:00.000Z",
                      "paused_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z",
                      "updated_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "flow",
                      "id": "flw_pqMp6g74MZoresPY",
                      "kind": "recovery",
                      "key": "default",
                      "name": "Recovery flow",
                      "status": "draft",
                      "holdout_percent": 0,
                      "test_version": null,
                      "live_version": null,
                      "unpublished_changes": [],
                      "mode_status": {
                        "livemode": false,
                        "state": "draft",
                        "version": null,
                        "published_at": null,
                        "unpublished_changes": []
                      },
                      "draft_updated_at": "2026-11-04T10:00:00.000Z",
                      "paused_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z",
                      "updated_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "flow",
                      "id": "flw_NZ3Jng8vP1kfQmtf",
                      "kind": "cancel",
                      "key": "default",
                      "name": "Cancel flow",
                      "status": "draft",
                      "holdout_percent": 5,
                      "test_version": null,
                      "live_version": null,
                      "unpublished_changes": [],
                      "mode_status": {
                        "livemode": false,
                        "state": "draft",
                        "version": null,
                        "published_at": null,
                        "unpublished_changes": []
                      },
                      "draft_updated_at": "2026-11-04T10:00:00.000Z",
                      "paused_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z",
                      "updated_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "flow",
                      "id": "flw_rVqAhXgThA4dJQdJ",
                      "kind": "winback",
                      "key": "default",
                      "name": "Win-back flow",
                      "status": "draft",
                      "holdout_percent": 5,
                      "test_version": null,
                      "live_version": null,
                      "unpublished_changes": [],
                      "mode_status": {
                        "livemode": false,
                        "state": "draft",
                        "version": null,
                        "published_at": null,
                        "unpublished_changes": []
                      },
                      "draft_updated_at": "2026-11-04T10:00:00.000Z",
                      "paused_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z",
                      "updated_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "flw_… or the kind of the default flow: prevention, recovery, cancel, winback."
        }
      ],
      "get": {
        "summary": "Retrieve a flow with its draft",
        "tags": [
          "Flows"
        ],
        "operationId": "getFlow",
        "responses": {
          "200": {
            "description": "the flow",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_ANSL3Lgcmq6LE2og",
                  "kind": "recovery",
                  "key": "default",
                  "name": "Recovery flow",
                  "status": "draft",
                  "holdout_percent": 0,
                  "test_version": null,
                  "live_version": null,
                  "unpublished_changes": [],
                  "mode_status": {
                    "livemode": false,
                    "state": "draft",
                    "version": null,
                    "published_at": null,
                    "unpublished_changes": []
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "inapp": {
                      "wall": {
                        "enabled": true,
                        "after_days": 7
                      },
                      "banner": {
                        "enabled": true,
                        "audience": "admins",
                        "dismiss_hours": 24
                      },
                      "annual_grace_days": 14
                    },
                    "emails": [
                      {
                        "day": 0
                      },
                      {
                        "day": 4
                      },
                      {
                        "day": 10
                      }
                    ],
                    "retries": {
                      "then": "cancel_at_period_end_and_winback",
                      "rules": [
                        {
                          "timing": "after_payday",
                          "attempts": 4,
                          "decline_code": "insufficient_funds"
                        },
                        {
                          "attempts": 0,
                          "email_now": true,
                          "decline_code": "expired_card"
                        },
                        {
                          "days": [
                            3,
                            5,
                            7
                          ],
                          "attempts": 3,
                          "decline_code": "do_not_honor"
                        },
                        {
                          "stop": true,
                          "email": false,
                          "attempts": 0,
                          "decline_code": "fraudulent"
                        }
                      ],
                      "max_days": 14
                    },
                    "send_window": {
                      "audience": null,
                      "quiet_hours": null,
                      "respect_customer_timezone": null
                    },
                    "email_settings": {
                      "tone": "friendly",
                      "review": "auto_check",
                      "ai_writes": true,
                      "languages": [
                        "en",
                        "fr",
                        "de",
                        "it",
                        "pt"
                      ]
                    },
                    "card_update_page": {
                      "methods": [
                        "card",
                        "apple_pay",
                        "google_pay",
                        "sepa_debit"
                      ],
                      "link_valid_days": 14,
                      "pay_open_invoice": true
                    }
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          },
          "404": {
            "description": "unknown flow",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Edit the draft",
        "tags": [
          "Flows"
        ],
        "operationId": "updateFlow",
        "description": "A section sent replaces that section of the draft. Changes run only once published.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "draft updated, unpublished changes listed",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_YJEtbe7bMLQa255h",
                  "kind": "recovery",
                  "key": "default",
                  "name": "Recovery flow",
                  "status": "draft",
                  "holdout_percent": 5,
                  "test_version": null,
                  "live_version": null,
                  "unpublished_changes": [
                    "retries"
                  ],
                  "mode_status": {
                    "livemode": false,
                    "state": "draft",
                    "version": null,
                    "published_at": null,
                    "unpublished_changes": [
                      "retries"
                    ]
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "inapp": {
                      "wall": {
                        "enabled": true,
                        "after_days": 7
                      },
                      "banner": {
                        "enabled": true,
                        "audience": "admins",
                        "dismiss_hours": 24
                      },
                      "annual_grace_days": 14
                    },
                    "emails": [
                      {
                        "day": 0
                      },
                      {
                        "day": 4
                      },
                      {
                        "day": 10
                      }
                    ],
                    "retries": {
                      "max_days": 10
                    },
                    "send_window": {
                      "audience": null,
                      "quiet_hours": null,
                      "respect_customer_timezone": null
                    },
                    "email_settings": {
                      "tone": "friendly",
                      "review": "auto_check",
                      "ai_writes": true,
                      "languages": [
                        "en",
                        "fr",
                        "de",
                        "it",
                        "pt"
                      ]
                    },
                    "card_update_page": {
                      "methods": [
                        "card",
                        "apple_pay",
                        "google_pay",
                        "sepa_debit"
                      ],
                      "link_valid_days": 14,
                      "pay_open_invoice": true
                    }
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          },
          "400": {
            "description": "unknown section",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "draft": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "name": {
                    "type": "string"
                  },
                  "holdout_percent": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 50
                  }
                }
              },
              "example": {
                "draft": {
                  "retries": {
                    "max_days": 10
                  }
                },
                "holdout_percent": 5
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/flows/{id}/publish": {
      "post": {
        "summary": "Publish the draft to test or live mode",
        "tags": [
          "Flows"
        ],
        "operationId": "publishFlow",
        "description": "to: test is \"Try in test mode\"; to: live is \"Publish\" (both modes then run this version).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "published",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_wrgKan8xHjAdgH4k",
                  "kind": "cancel",
                  "key": "default",
                  "name": "Cancel flow",
                  "status": "live",
                  "holdout_percent": 5,
                  "test_version": {
                    "id": "flv_hPABNrYmFDsYS7G8",
                    "number": 1,
                    "published_at": "2026-11-04T10:00:00.000Z",
                    "published_to": "live"
                  },
                  "live_version": {
                    "id": "flv_hPABNrYmFDsYS7G8",
                    "number": 1,
                    "published_at": "2026-11-04T10:00:00.000Z",
                    "published_to": "live"
                  },
                  "unpublished_changes": [],
                  "mode_status": {
                    "livemode": false,
                    "state": "published",
                    "version": 1,
                    "published_at": "2026-11-04T10:00:00.000Z",
                    "unpublished_changes": []
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "pause": {
                      "inapp": "none"
                    },
                    "steps": [
                      "reason",
                      "offer",
                      "feedback",
                      "confirmation"
                    ],
                    "texts": {},
                    "design": {
                      "format": "modal",
                      "preset": "light",
                      "tokens": {},
                      "custom_css": ""
                    },
                    "reasons": [
                      {
                        "key": "too_expensive",
                        "labels": {
                          "de": "Es ist zu teuer",
                          "en": "It's too expensive",
                          "fr": "C'est trop cher",
                          "it": "È troppo caro",
                          "pt": "É demasiado caro"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "missing_feature",
                        "labels": {
                          "de": "Eine Funktion fehlt",
                          "en": "A feature is missing",
                          "fr": "Il manque une fonctionnalité",
                          "it": "Manca una funzionalità",
                          "pt": "Falta uma funcionalidade"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "not_using",
                        "labels": {
                          "de": "Ich nutze es zu wenig",
                          "en": "I don't use it enough",
                          "fr": "Je ne l'utilise pas assez",
                          "it": "Non lo uso abbastanza",
                          "pt": "Não o uso o suficiente"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "switching",
                        "labels": {
                          "de": "Ich wechsle zu einem anderen Tool",
                          "en": "I'm switching to another tool",
                          "fr": "Je passe à un autre outil",
                          "it": "Passo a un altro strumento",
                          "pt": "Vou mudar para outra ferramenta"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "temporary",
                        "labels": {
                          "de": "Ich brauche nur eine Pause",
                          "en": "I only need a break",
                          "fr": "J'ai juste besoin d'une pause",
                          "it": "Ho solo bisogno di una pausa",
                          "pt": "Só preciso de uma pausa"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "project_finished",
                        "labels": {
                          "de": "Mein Projekt ist abgeschlossen",
                          "en": "My project is finished",
                          "fr": "Mon projet est terminé",
                          "it": "Il mio progetto è finito",
                          "pt": "O meu projeto terminou"
                        },
                        "offer_id": null
                      },
                      {
                        "key": "other",
                        "labels": {
                          "de": "Etwas anderes",
                          "en": "Something else",
                          "fr": "Autre chose",
                          "it": "Altro",
                          "pt": "Outra coisa"
                        },
                        "offer_id": null,
                        "free_text": true
                      }
                    ],
                    "feedback": {
                      "enabled": true,
                      "required": false
                    },
                    "cancel_mode": "at_period_end",
                    "review_requests": {
                      "enabled": false,
                      "happy_reasons": [
                        "project_finished",
                        "too_expensive",
                        "temporary"
                      ],
                      "follow_up_email_days": 2
                    },
                    "offers_once_per_customer_per_year": true
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "enum": [
                      "test",
                      "live"
                    ]
                  }
                }
              },
              "example": {
                "to": "live"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/flows/{id}/discard": {
      "post": {
        "summary": "Discard the unpublished changes",
        "tags": [
          "Flows"
        ],
        "operationId": "discardFlowDraft",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "draft back to the last version",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_qUMZ9KGuvjKnxmww",
                  "kind": "recovery",
                  "key": "default",
                  "name": "Recovery flow",
                  "status": "draft",
                  "holdout_percent": 0,
                  "test_version": null,
                  "live_version": null,
                  "unpublished_changes": [],
                  "mode_status": {
                    "livemode": false,
                    "state": "draft",
                    "version": null,
                    "published_at": null,
                    "unpublished_changes": []
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "retries": {
                      "rules": [
                        {
                          "decline_code": "insufficient_funds",
                          "attempts": 4,
                          "timing": "after_payday"
                        },
                        {
                          "decline_code": "expired_card",
                          "attempts": 0,
                          "email_now": true
                        },
                        {
                          "decline_code": "do_not_honor",
                          "attempts": 3,
                          "days": [
                            3,
                            5,
                            7
                          ]
                        },
                        {
                          "decline_code": "fraudulent",
                          "attempts": 0,
                          "stop": true,
                          "email": false
                        }
                      ],
                      "max_days": 14,
                      "then": "cancel_at_period_end_and_winback"
                    },
                    "emails": [
                      {
                        "day": 0
                      },
                      {
                        "day": 4
                      },
                      {
                        "day": 10
                      }
                    ],
                    "email_settings": {
                      "ai_writes": true,
                      "tone": "friendly",
                      "review": "auto_check",
                      "languages": [
                        "en",
                        "fr",
                        "de",
                        "it",
                        "pt"
                      ]
                    },
                    "card_update_page": {
                      "link_valid_days": 14,
                      "pay_open_invoice": true,
                      "methods": [
                        "card",
                        "apple_pay",
                        "google_pay",
                        "sepa_debit"
                      ]
                    },
                    "inapp": {
                      "banner": {
                        "enabled": true,
                        "audience": "admins",
                        "dismiss_hours": 24
                      },
                      "wall": {
                        "enabled": true,
                        "after_days": 7
                      },
                      "annual_grace_days": 14
                    },
                    "send_window": {
                      "audience": null,
                      "respect_customer_timezone": null,
                      "quiet_hours": null
                    }
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/versions": {
      "get": {
        "summary": "List published versions, newest first",
        "tags": [
          "Flows"
        ],
        "operationId": "listFlowVersions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "versions",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "flow_version",
                      "id": "flv_uLhBgP6hNvvgKQbm",
                      "flow": "flw_Wwa1oeVCBHng5M6m",
                      "number": 2,
                      "config": {
                        "inapp": {
                          "wall": {
                            "enabled": true,
                            "after_days": 7
                          },
                          "banner": {
                            "enabled": true,
                            "audience": "admins",
                            "dismiss_hours": 24
                          },
                          "annual_grace_days": 14
                        },
                        "emails": [
                          {
                            "day": 0
                          },
                          {
                            "day": 4
                          },
                          {
                            "day": 10
                          }
                        ],
                        "retries": {
                          "then": "cancel_at_period_end_and_winback",
                          "rules": [
                            {
                              "timing": "after_payday",
                              "attempts": 4,
                              "decline_code": "insufficient_funds"
                            },
                            {
                              "attempts": 0,
                              "email_now": true,
                              "decline_code": "expired_card"
                            },
                            {
                              "days": [
                                3,
                                5,
                                7
                              ],
                              "attempts": 3,
                              "decline_code": "do_not_honor"
                            },
                            {
                              "stop": true,
                              "email": false,
                              "attempts": 0,
                              "decline_code": "fraudulent"
                            }
                          ],
                          "max_days": 14
                        },
                        "send_window": {
                          "audience": null,
                          "quiet_hours": null,
                          "respect_customer_timezone": null
                        },
                        "email_settings": {
                          "tone": "friendly",
                          "review": "auto_check",
                          "ai_writes": true,
                          "languages": [
                            "en",
                            "fr",
                            "de",
                            "it",
                            "pt"
                          ]
                        },
                        "card_update_page": {
                          "methods": [
                            "card",
                            "apple_pay",
                            "google_pay",
                            "sepa_debit"
                          ],
                          "link_valid_days": 14,
                          "pay_open_invoice": true
                        }
                      },
                      "holdout_percent": 0,
                      "published_to": "test",
                      "published_by": "system",
                      "published_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "flow_version",
                      "id": "flv_VGm8keAZNkfA8TPw",
                      "flow": "flw_Wwa1oeVCBHng5M6m",
                      "number": 1,
                      "config": {
                        "inapp": {
                          "wall": {
                            "enabled": true,
                            "after_days": 7
                          },
                          "banner": {
                            "enabled": true,
                            "audience": "admins",
                            "dismiss_hours": 24
                          },
                          "annual_grace_days": 14
                        },
                        "emails": [
                          {
                            "day": 0
                          },
                          {
                            "day": 4
                          },
                          {
                            "day": 10
                          }
                        ],
                        "retries": {
                          "then": "cancel_at_period_end_and_winback",
                          "rules": [
                            {
                              "timing": "after_payday",
                              "attempts": 4,
                              "decline_code": "insufficient_funds"
                            },
                            {
                              "attempts": 0,
                              "email_now": true,
                              "decline_code": "expired_card"
                            },
                            {
                              "days": [
                                3,
                                5,
                                7
                              ],
                              "attempts": 3,
                              "decline_code": "do_not_honor"
                            },
                            {
                              "stop": true,
                              "email": false,
                              "attempts": 0,
                              "decline_code": "fraudulent"
                            }
                          ],
                          "max_days": 14
                        },
                        "send_window": {
                          "audience": null,
                          "quiet_hours": null,
                          "respect_customer_timezone": null
                        },
                        "email_settings": {
                          "tone": "friendly",
                          "review": "auto_check",
                          "ai_writes": true,
                          "languages": [
                            "en",
                            "fr",
                            "de",
                            "it",
                            "pt"
                          ]
                        },
                        "card_update_page": {
                          "methods": [
                            "card",
                            "apple_pay",
                            "google_pay",
                            "sepa_debit"
                          ],
                          "link_valid_days": 14,
                          "pay_open_invoice": true
                        }
                      },
                      "holdout_percent": 0,
                      "published_to": "test",
                      "published_by": "system",
                      "published_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowVersionList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/versions/{number}/restore": {
      "post": {
        "summary": "Put an older version back in the draft",
        "tags": [
          "Flows"
        ],
        "operationId": "restoreFlowVersion",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "restored in the draft (publish to apply)",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_bHzwVnLVo1KxDxu6",
                  "kind": "recovery",
                  "key": "default",
                  "name": "Recovery flow",
                  "status": "test",
                  "holdout_percent": 0,
                  "test_version": {
                    "id": "flv_ymeANEcX3A1TunN7",
                    "number": 2,
                    "published_at": "2026-11-04T10:00:00.000Z",
                    "published_to": "test"
                  },
                  "live_version": null,
                  "unpublished_changes": [
                    "retries"
                  ],
                  "mode_status": {
                    "livemode": false,
                    "state": "published",
                    "version": 2,
                    "published_at": "2026-11-04T10:00:00.000Z",
                    "unpublished_changes": [
                      "retries"
                    ]
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "inapp": {
                      "wall": {
                        "enabled": true,
                        "after_days": 7
                      },
                      "banner": {
                        "enabled": true,
                        "audience": "admins",
                        "dismiss_hours": 24
                      },
                      "annual_grace_days": 14
                    },
                    "emails": [
                      {
                        "day": 0
                      },
                      {
                        "day": 4
                      },
                      {
                        "day": 10
                      }
                    ],
                    "retries": {
                      "then": "cancel_at_period_end_and_winback",
                      "rules": [
                        {
                          "timing": "after_payday",
                          "attempts": 4,
                          "decline_code": "insufficient_funds"
                        },
                        {
                          "attempts": 0,
                          "email_now": true,
                          "decline_code": "expired_card"
                        },
                        {
                          "days": [
                            3,
                            5,
                            7
                          ],
                          "attempts": 3,
                          "decline_code": "do_not_honor"
                        },
                        {
                          "stop": true,
                          "email": false,
                          "attempts": 0,
                          "decline_code": "fraudulent"
                        }
                      ],
                      "max_days": 14
                    },
                    "send_window": {
                      "audience": null,
                      "quiet_hours": null,
                      "respect_customer_timezone": null
                    },
                    "email_settings": {
                      "tone": "friendly",
                      "review": "auto_check",
                      "ai_writes": true,
                      "languages": [
                        "en",
                        "fr",
                        "de",
                        "it",
                        "pt"
                      ]
                    },
                    "card_update_page": {
                      "methods": [
                        "card",
                        "apple_pay",
                        "google_pay",
                        "sepa_debit"
                      ],
                      "link_valid_days": 14,
                      "pay_open_invoice": true
                    }
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/pause": {
      "post": {
        "summary": "Pause a flow (no new run)",
        "tags": [
          "Flows"
        ],
        "operationId": "pauseFlow",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "paused",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_owZ77WgHmyWTS5hn",
                  "kind": "winback",
                  "key": "default",
                  "name": "Win-back flow",
                  "status": "paused",
                  "holdout_percent": 5,
                  "test_version": null,
                  "live_version": null,
                  "unpublished_changes": [],
                  "mode_status": {
                    "livemode": false,
                    "state": "paused",
                    "version": null,
                    "published_at": null,
                    "unpublished_changes": []
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "offer": {
                      "label": "1 month free",
                      "promo_code": null
                    },
                    "stop_on": [
                      "reply",
                      "resubscribe",
                      "unsubscribe"
                    ],
                    "sequence": [
                      {
                        "day": 30,
                        "kind": "d30"
                      },
                      {
                        "day": 60,
                        "kind": "d60"
                      },
                      {
                        "day": 90,
                        "kind": "d90"
                      }
                    ],
                    "never_after": [
                      "refund",
                      "fraud",
                      "gdpr_erasure"
                    ],
                    "send_window": {
                      "audience": null,
                      "quiet_hours": null,
                      "respect_customer_timezone": null
                    },
                    "resubscribe_url": null,
                    "max_churn_age_days": 7
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          },
          "403": {
            "description": "restricted key with flows:read only",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/resume": {
      "post": {
        "summary": "Resume a paused flow",
        "tags": [
          "Flows"
        ],
        "operationId": "resumeFlow",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "resumed",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow",
                  "id": "flw_RnhboVHZYcVRnCsL",
                  "kind": "winback",
                  "key": "default",
                  "name": "Win-back flow",
                  "status": "draft",
                  "holdout_percent": 5,
                  "test_version": null,
                  "live_version": null,
                  "unpublished_changes": [],
                  "mode_status": {
                    "livemode": false,
                    "state": "draft",
                    "version": null,
                    "published_at": null,
                    "unpublished_changes": []
                  },
                  "draft_updated_at": "2026-11-04T10:00:00.000Z",
                  "paused_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "draft": {
                    "offer": {
                      "label": "1 month free",
                      "promo_code": null
                    },
                    "stop_on": [
                      "reply",
                      "resubscribe",
                      "unsubscribe"
                    ],
                    "sequence": [
                      {
                        "day": 30,
                        "kind": "d30"
                      },
                      {
                        "day": 60,
                        "kind": "d60"
                      },
                      {
                        "day": 90,
                        "kind": "d90"
                      }
                    ],
                    "never_after": [
                      "refund",
                      "fraud",
                      "gdpr_erasure"
                    ],
                    "send_window": {
                      "audience": null,
                      "quiet_hours": null,
                      "respect_customer_timezone": null
                    },
                    "resubscribe_url": null,
                    "max_churn_age_days": 7
                  },
                  "logos": []
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/runs": {
      "get": {
        "summary": "Customers in this flow (current mode)",
        "tags": [
          "Flows"
        ],
        "operationId": "listFlowRuns",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "succeeded",
                "failed",
                "stopped"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "runs of the mode, with their customer",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "flow_run",
                      "id": "run_DDiKSSzFHtYhREuC",
                      "livemode": false,
                      "flow": "flw_V7HicHjaunR9yXum",
                      "version": 1,
                      "customer": {
                        "id": "cst_77ZMsUw9nrd8gjCi",
                        "name": "Lena",
                        "email": "customer1@acme-corp.dev",
                        "mrr_cents": 0,
                        "currency": null
                      },
                      "subject": null,
                      "holdout": false,
                      "state": "active",
                      "current_step": "entered",
                      "outcome": null,
                      "outcome_at": null,
                      "outcome_mrr_cents": null,
                      "outcome_cents": null,
                      "currency": null,
                      "confirmation": null,
                      "stop_reason": null,
                      "started_at": "2026-11-04T10:00:00.000Z",
                      "ended_at": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowRunList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/inapp/state": {
      "get": {
        "summary": "What the customer's app shows: payment banner and wall",
        "tags": [
          "In-app"
        ],
        "operationId": "getInappState",
        "description": "From the browser: publishable key + customer token (HS256 { kind, value, exp } signed with your widget secret) + an allowed Origin. From a server: secret key and ?customer=.",
        "parameters": [
          {
            "name": "X-NoChurn-Customer-Token",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "enum": [
              "en",
              "fr",
              "de",
              "it",
              "pt"
            ],
            "description": "Texts language; default: the customer's, then the account's.:\n * `en` \n * `fr` \n * `de` \n * `it` \n * `pt` \n ",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "a failing payment: banner on, wall after 7 days",
            "content": {
              "application/json": {
                "example": {
                  "object": "inapp_state",
                  "customer": "cst_vDH4Ubcpyj5pzVs1",
                  "failing_since": "2026-11-04T10:00:00Z",
                  "locale": "fr",
                  "texts": {
                    "banner_text": "Votre dernier paiement de €49.00 n'est pas passé. Mettez à jour votre moyen de paiement pour garder votre compte.",
                    "banner_cta": "Payer maintenant",
                    "dismiss": "Masquer",
                    "banner_days_left": "Plus que {{days}} jours avant la suspension de votre compte.",
                    "banner_last_day": "Dernier jour avant la suspension de votre compte.",
                    "wall_title": "Votre compte est suspendu",
                    "wall_text": "Votre compte est suspendu jusqu'au paiement de €49.00.",
                    "wall_cta": "Mettre à jour le moyen de paiement",
                    "pay_cta": "Mettre à jour la carte et payer €49.00",
                    "amount_due": "Montant dû",
                    "card_expired": "Expirée {{exp}}",
                    "decline_expired": "Carte expirée",
                    "decline_insufficient_funds": "Fonds insuffisants",
                    "decline_authentication_required": "Votre banque demande de valider le paiement",
                    "decline_declined": "Refusée par votre banque",
                    "checking_payment": "Vérification du paiement…",
                    "back_in_title": "Vous revoilà",
                    "back_in_text": "Paiement reçu. Merci !",
                    "data_safe": "Vos données sont en sécurité. Rien n'est supprimé.",
                    "paused_banner_text": "Votre abonnement est en pause jusqu'au {{date}}.",
                    "paused_title": "Votre abonnement est en pause",
                    "paused_text": "Aucun prélèvement pendant la pause. Vos données sont conservées.",
                    "paused_resumes": "Reprise automatique dans {{days}} jours, à €49.00.",
                    "resume_cta": "Reprendre maintenant",
                    "resuming": "Reprise…",
                    "resumed_title": "Bon retour",
                    "resumed_text": "Votre abonnement est de nouveau actif.",
                    "cancel_now": "Résilier mon abonnement"
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Acme",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "access": "full",
                  "access_reason": null,
                  "banner": {
                    "show": true,
                    "kind": "payment_failed",
                    "audience": "admins",
                    "text": "Votre dernier paiement de €49.00 n'est pas passé. Mettez à jour votre moyen de paiement pour garder votre compte.",
                    "cta_url": "http://localhost:3000/c/okIJi02RYcm_ylJZATU-2KwNQpxXw64R",
                    "cta_label": "Payer maintenant",
                    "dismiss_label": "Masquer",
                    "dismissible_for_hours": 24,
                    "restricts_at": "2026-11-11T10:00:00Z",
                    "days_left": 7,
                    "details": {
                      "kind": "payment_failed",
                      "amount_due_cents": 4900,
                      "currency": "eur",
                      "invoice_due_at": null,
                      "payment_method": null,
                      "decline": "expired"
                    }
                  },
                  "wall": {
                    "show": false,
                    "kind": "payment_failed",
                    "text": "Votre compte est suspendu jusqu'au paiement de €49.00.",
                    "cta_url": "http://localhost:3000/c/okIJi02RYcm_ylJZATU-2KwNQpxXw64R",
                    "cta_label": "Mettre à jour le moyen de paiement",
                    "shows_after_days": 7,
                    "cancel": {
                      "label": "Résilier mon abonnement",
                      "fallback_url": "http://localhost:3000/k/pk_test_bCx4srPPXRCaL7ykidyP3qc498kijZxq"
                    },
                    "details": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/InappState"
                }
              }
            }
          },
          "401": {
            "description": "a token signed with another secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "an origin that is not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/inapp/dismiss": {
      "post": {
        "summary": "Hide the banner for 24 hours",
        "tags": [
          "In-app"
        ],
        "operationId": "dismissInappBanner",
        "parameters": [
          {
            "name": "X-NoChurn-Customer-Token",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "hidden; the wall cannot be dismissed",
            "content": {
              "application/json": {
                "example": {
                  "object": "inapp_state",
                  "customer": "cst_vCz9mjyDe3XdmdTJ",
                  "failing_since": "2026-11-04T10:00:00Z",
                  "locale": "en",
                  "texts": {
                    "banner_text": "Your last payment of €49.00 didn't go through. Update your payment method to keep your account.",
                    "banner_cta": "Update payment method",
                    "dismiss": "Dismiss",
                    "banner_days_left": "{{days}} days left before your account is on hold.",
                    "banner_last_day": "Last day before your account is on hold.",
                    "wall_title": "Your account is on hold",
                    "wall_text": "Your account is on hold until the payment of €49.00 goes through.",
                    "wall_cta": "Update payment method",
                    "pay_cta": "Update card and pay €49.00",
                    "amount_due": "Amount due",
                    "card_expired": "Expired {{exp}}",
                    "decline_expired": "Card expired",
                    "decline_insufficient_funds": "Insufficient funds",
                    "decline_authentication_required": "Your bank needs you to approve the payment",
                    "decline_declined": "Declined by your bank",
                    "checking_payment": "Checking your payment…",
                    "back_in_title": "You're back in",
                    "back_in_text": "Payment received. Thanks!",
                    "data_safe": "Your data is safe. Nothing is deleted.",
                    "paused_banner_text": "Your plan is paused until {{date}}.",
                    "paused_title": "Your plan is paused",
                    "paused_text": "No charges while it's paused. Your data is kept.",
                    "paused_resumes": "Resumes by itself in {{days}} days, at €49.00.",
                    "resume_cta": "Resume now",
                    "resuming": "Resuming…",
                    "resumed_title": "Welcome back",
                    "resumed_text": "Your plan is active again.",
                    "cancel_now": "Cancel my subscription"
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Account 1",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "access": "full",
                  "access_reason": null,
                  "banner": {
                    "show": false,
                    "kind": "payment_failed",
                    "audience": "admins",
                    "text": "Your last payment of €49.00 didn't go through. Update your payment method to keep your account.",
                    "cta_url": "http://localhost:3000/c/XjLe-todmcu_jXSv4LVBfduidG16OE8l",
                    "cta_label": "Update payment method",
                    "dismiss_label": "Dismiss",
                    "dismissible_for_hours": 24,
                    "restricts_at": "2026-11-11T10:00:00Z",
                    "days_left": 7,
                    "details": null
                  },
                  "wall": {
                    "show": false,
                    "kind": "payment_failed",
                    "text": "Your account is on hold until the payment of €49.00 goes through.",
                    "cta_url": "http://localhost:3000/c/XjLe-todmcu_jXSv4LVBfduidG16OE8l",
                    "cta_label": "Update payment method",
                    "shows_after_days": 7,
                    "cancel": {
                      "label": "Cancel my subscription",
                      "fallback_url": "http://localhost:3000/k/pk_test_BBBxnJuz1rBMZ7B1jEkAwn4sKy8napzG"
                    },
                    "details": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/InappState"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/integrations": {
      "get": {
        "summary": "Connected tools",
        "tags": [
          "Integrations"
        ],
        "operationId": "listIntegrations",
        "responses": {
          "200": {
            "description": "credentials are never returned",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "has_more": false,
                  "next_cursor": null,
                  "data": [
                    {
                      "object": "integration",
                      "id": "itg_GryteF3meCbHffEf",
                      "kind": "slack",
                      "status": "active",
                      "external_account": "Acme",
                      "config": {},
                      "last_synced_at": null,
                      "error": null,
                      "conversations_count": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegrationList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Connect Crisp (website or plugin token) or Gorgias (REST API key)",
        "tags": [
          "Integrations"
        ],
        "operationId": "connectCrisp",
        "description": "Crisp: `identifier`, `key` (a website token from Settings › Workspace Settings › Advanced configuration › API Token, or a Marketplace plugin token), `website_id`. Gorgias: `domain` (the part before .gorgias.com), `email` and `api_key` (Settings › Account › REST API). Read only; credentials are encrypted and never returned.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "connected (read only)",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration",
                  "id": "itg_PTnp2N7f2MPagEv6",
                  "kind": "crisp",
                  "status": "active",
                  "external_account": "Acme",
                  "config": {},
                  "last_synced_at": null,
                  "error": null,
                  "conversations_count": 0,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          },
          "400": {
            "description": "Crisp refused the credentials or does not know the website",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "kind",
                      "identifier",
                      "key",
                      "website_id"
                    ],
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "crisp"
                        ]
                      },
                      "identifier": {
                        "type": "string"
                      },
                      "key": {
                        "type": "string"
                      },
                      "website_id": {
                        "type": "string"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "kind",
                      "domain",
                      "email",
                      "api_key"
                    ],
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "gorgias"
                        ]
                      },
                      "domain": {
                        "type": "string",
                        "example": "acme"
                      },
                      "email": {
                        "type": "string"
                      },
                      "api_key": {
                        "type": "string"
                      }
                    }
                  }
                ]
              },
              "example": {
                "kind": "crisp",
                "identifier": "id_test",
                "key": "key_test",
                "website_id": "8c1f3f52-2a8a-4d6b-9a0e-6f1f2c3d4e5f"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/integrations/{kind}/authorize": {
      "parameters": [
        {
          "name": "kind",
          "in": "path",
          "schema": {
            "type": "string",
            "enum": [
              "intercom",
              "zendesk",
              "hubspot",
              "helpscout",
              "slack"
            ]
          },
          "required": true
        }
      ],
      "post": {
        "summary": "OAuth URL of a support tool or Slack",
        "tags": [
          "Integrations"
        ],
        "operationId": "authorizeIntegration",
        "description": "The dashboard only redirects to `url`. Zendesk needs the account's `subdomain` (connect as a Zendesk admin). Intercom takes an optional `region` (`us`, `eu`, `au`: where the workspace is hosted; `us` by default) for its authorization host.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "the URL",
            "content": {
              "application/json": {
                "example": {
                  "object": "authorize_url",
                  "kind": "intercom",
                  "url": "https://app.intercom.com/oauth?client_id=ic_test&state=eyJfcmFpbHMiOnsiZGF0YSI6eyJhY2NvdW50X2lkIjoiYWNjX3F2YXRjSlR6d25DWjRaWUoiLCJ1c2VyX2lkIjpudWxsLCJraW5kIjoiaW50ZXJjb20iLCJub25jZSI6IjE2OWU1ZDQ5MTcxNGZjYmQifSwiZXhwIjoiMjAyNi0xMS0wNFQxMDozMDowMC4wMDBaIiwicHVyIjoiaW50ZWdyYXRpb24ifX0%3D--7d97ba0133a2d258517598968809f921d24c10e0"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegrationAuthorizeUrl"
                }
              }
            }
          },
          "400": {
            "description": "unknown region",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subdomain": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string",
                    "enum": [
                      "us",
                      "eu",
                      "au"
                    ]
                  }
                }
              },
              "example": {}
            }
          }
        }
      }
    },
    "/v1/integrations/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Slack channel",
        "tags": [
          "Integrations"
        ],
        "operationId": "updateIntegration",
        "parameters": [],
        "responses": {
          "200": {
            "description": "updated",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration",
                  "id": "itg_WFTBKq2vf2R8QZMo",
                  "kind": "slack",
                  "status": "active",
                  "external_account": "Acme",
                  "config": {
                    "channel": "#retention"
                  },
                  "last_synced_at": null,
                  "error": null,
                  "conversations_count": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "config": {
                    "type": "object",
                    "properties": {
                      "channel": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "config": {
                  "channel": "#retention"
                }
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Disconnect",
        "tags": [
          "Integrations"
        ],
        "operationId": "disconnectIntegration",
        "responses": {
          "200": {
            "description": "credentials erased",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration",
                  "id": "itg_tRyqFBV8V6BgHf6d",
                  "kind": "slack",
                  "status": "revoked",
                  "external_account": "Acme",
                  "config": {},
                  "last_synced_at": null,
                  "error": null,
                  "conversations_count": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/integrations/{id}/sync": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Sync now (support tools)",
        "tags": [
          "Integrations"
        ],
        "operationId": "syncIntegration",
        "responses": {
          "202": {
            "description": "queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "integration",
                  "id": "itg_Pa34cY9hMexK8b2u",
                  "kind": "intercom",
                  "status": "active",
                  "external_account": "Acme",
                  "config": {},
                  "last_synced_at": null,
                  "error": null,
                  "conversations_count": 0,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          },
          "409": {
            "description": "not connected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/feedback": {
      "get": {
        "summary": "Feedback themes and latest answers",
        "tags": [
          "Data & AI"
        ],
        "operationId": "getFeedback",
        "responses": {
          "200": {
            "description": "nothing yet",
            "content": {
              "application/json": {
                "example": {
                  "object": "feedback",
                  "answers_90d": 0,
                  "run": null,
                  "items": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Feedback"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/feedback/refresh": {
      "post": {
        "summary": "Theme again (once an hour)",
        "tags": [
          "Data & AI"
        ],
        "operationId": "refreshFeedback",
        "responses": {
          "202": {
            "description": "queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "feedback_refresh",
                  "queued": true
                },
                "schema": {
                  "$ref": "#/components/schemas/FeedbackRefresh"
                }
              }
            }
          },
          "429": {
            "description": "already requested this hour (counted from the click)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/insight": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "AI analysis of a customer",
        "tags": [
          "Customers"
        ],
        "operationId": "analyzeCustomer",
        "description": "Without name, email or sensitive attributes; every number comes from the profile. Fresh for 7 days unless an important event for the customer comes after it (the profile's ai_analysis_status): a fresh analysis is returned without an AI call. One at a time per customer, 200 AI calls a day per account.",
        "responses": {
          "200": {
            "description": "the analysis",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer_insight",
                  "id": "cai_Dbnz8NirwtHM5xPz",
                  "summary": "On the Pro plan.",
                  "signals": [
                    "Pro"
                  ],
                  "next_step": "Check in",
                  "sources": [
                    "custom_data"
                  ],
                  "computed_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomerInsight"
                }
              }
            }
          },
          "422": {
            "description": "not enough activity for an analysis (insufficient_data, no AI call)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/live_requests": {
      "post": {
        "summary": "Ask a human to do something in live mode (AI apps)",
        "tags": [
          "AI apps"
        ],
        "operationId": "createLiveRequest",
        "description": "OAuth tokens only (configure scope, plus the scope the operation needs in test mode: sandbox for customers, usage, subscriptions and payments). AI apps never change live mode directly: they ask for one operation (action) with its params (one schema per operation, see LiveRequestCreate) and give approval_url to an owner or admin. NoChurn checks the params, looks the target up in live mode, writes its own summary and captures what must still hold at approval (precondition). Nothing runs until a human approves it in the dashboard; it then runs in live mode as that human. Asked in any mode (the NoChurn-Mode header is ignored here). The same pending request asked again is returned as is. Never possible: the team, AI app access, 2FA, NoChurn's own plan, editing or revoking existing keys, deleting the account.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "pending, with its approval link",
            "content": {
              "application/json": {
                "example": {
                  "object": "live_request",
                  "id": "lrq_7y3fem7wpcHdUx1s",
                  "action": "create_live_api_key",
                  "title": "Create a live API key",
                  "summary": "Create a live secret (nc_live_…) API key named “Production server”, full access",
                  "params": {
                    "name": "Production server",
                    "kind": "secret",
                    "access": "full",
                    "permissions": {}
                  },
                  "precondition": null,
                  "reason": "Integration verified in test mode",
                  "status": "pending",
                  "requested_by": {
                    "type": "ai_agent",
                    "name": "Claude Code",
                    "grant_id": "ogr_A9AUMTA8qUFrqLLZ"
                  },
                  "approval_url": "http://localhost:5173/live-requests/lrq_7y3fem7wpcHdUx1s?account=acc_itN4DgVRL8A9caPs",
                  "decided_by": null,
                  "decided_at": null,
                  "result": {},
                  "error": null,
                  "api_key": null,
                  "response": null,
                  "expires_at": "2026-11-11T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/LiveRequest"
                }
              }
            }
          },
          "400": {
            "description": "unknown action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "an API key (act in live mode directly instead)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LiveRequestCreate"
              },
              "example": {
                "action": "create_live_api_key",
                "params": {
                  "name": "Production server"
                },
                "reason": "Integration verified in test mode"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "List live requests",
        "tags": [
          "AI apps"
        ],
        "operationId": "listLiveRequests",
        "description": "An AI app sees its own requests; the dashboard sees every request of the account.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "rejected",
                "failed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the requests",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "live_request",
                      "id": "lrq_x1S3mBZzebRmeJkX",
                      "action": "set_billing_source",
                      "title": "Change the billing source",
                      "summary": "Set the billing source to your own billing (API) (test and live modes)",
                      "params": {
                        "billing_source": "api"
                      },
                      "precondition": {
                        "kind": "current_value",
                        "object": "billing_source",
                        "id": null,
                        "expected": "stripe",
                        "status": "holds",
                        "description": "The billing source must still be stripe."
                      },
                      "reason": "Ready for production",
                      "status": "pending",
                      "requested_by": {
                        "type": "ai_agent",
                        "name": "Claude Code",
                        "grant_id": "ogr_9Gp8DnTNkPEsTAAU"
                      },
                      "approval_url": "http://localhost:5173/live-requests/lrq_x1S3mBZzebRmeJkX?account=acc_n31kFwAhaVTBRdpZ",
                      "decided_by": null,
                      "decided_at": null,
                      "result": {},
                      "error": null,
                      "api_key": null,
                      "response": null,
                      "expires_at": "2026-11-11T10:00:00.000Z",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/LiveRequestList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/live_requests/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Retrieve a live request",
        "tags": [
          "AI apps"
        ],
        "operationId": "getLiveRequest",
        "description": "AI apps poll it to know whether a human approved.",
        "responses": {
          "200": {
            "description": "the request",
            "content": {
              "application/json": {
                "example": {
                  "object": "live_request",
                  "id": "lrq_a29dDA8tYCJq8KTP",
                  "action": "publish_flow_live",
                  "title": "Publish a flow to live mode",
                  "summary": "Publish the current draft of Cancel flow to live mode (customers in live mode get it)",
                  "params": {
                    "flow_id": "flw_K13CEko818VgKSAc",
                    "flow_kind": "cancel",
                    "flow_name": "Cancel flow"
                  },
                  "precondition": {
                    "kind": "draft_digest",
                    "object": "flow",
                    "id": "flw_K13CEko818VgKSAc",
                    "expected": null,
                    "status": "holds",
                    "description": "The flow draft must be the one the app asked to publish."
                  },
                  "reason": "Ready for production",
                  "status": "pending",
                  "requested_by": {
                    "type": "ai_agent",
                    "name": "Claude Code",
                    "grant_id": "ogr_CxuaJ9mcH429DzBw"
                  },
                  "approval_url": "http://localhost:5173/live-requests/lrq_a29dDA8tYCJq8KTP?account=acc_ptg1cVcxU237ywmi",
                  "decided_by": null,
                  "decided_at": null,
                  "result": {},
                  "error": null,
                  "api_key": null,
                  "response": null,
                  "expires_at": "2026-11-11T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/LiveRequest"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/live_requests/{id}/approve": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Approve a live request (dashboard, owner or admin)",
        "tags": [
          "AI apps"
        ],
        "operationId": "approveLiveRequest",
        "description": "Runs the operation now, as the approver, in live mode (its Events carry via: the AI app). Its API answer is returned once, to the approver only, in response (and api_key for create_live_api_key): a live key's token, a webhook endpoint's signing secret. Fails (status failed, error) without changing anything if the precondition no longer holds or the operation is refused.",
        "parameters": [
          {
            "name": "X-Requested-With",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "approved: the live key, shown once",
            "content": {
              "application/json": {
                "example": {
                  "object": "live_request",
                  "id": "lrq_FpLTMa8X3rjugaNz",
                  "action": "create_live_api_key",
                  "title": "Create a live API key",
                  "summary": "Create a live secret (nc_live_…) API key named “Production server”, full access",
                  "params": {
                    "kind": "secret",
                    "name": "Production server",
                    "access": "full",
                    "permissions": {}
                  },
                  "precondition": null,
                  "reason": "Ready for production",
                  "status": "approved",
                  "requested_by": {
                    "type": "ai_agent",
                    "name": "Claude Code",
                    "grant_id": "ogr_TH3UBPgpw5dZYQcm"
                  },
                  "approval_url": "http://localhost:5173/live-requests/lrq_FpLTMa8X3rjugaNz?account=acc_SHWzbh3KJWUUN6ez",
                  "decided_by": {
                    "id": "usr_4NwxZDGRUAd3xyBn",
                    "email": "user1@example.test"
                  },
                  "decided_at": "2026-11-04T10:00:00.000Z",
                  "result": {
                    "object": "api_key",
                    "id": "key_wjS3tDF9X8143ppr",
                    "api_key_id": "key_wjS3tDF9X8143ppr"
                  },
                  "error": null,
                  "api_key": {
                    "object": "api_key",
                    "id": "key_wjS3tDF9X8143ppr",
                    "name": "Production server",
                    "kind": "secret",
                    "mode": "live",
                    "access": "full",
                    "permissions": {
                      "account": "write",
                      "api_keys": "write",
                      "team": "write",
                      "events": "write",
                      "customers": "write",
                      "billing": "write",
                      "stripe_connections": "write",
                      "logs": "write",
                      "flows": "write",
                      "email_domains": "write",
                      "messages": "write",
                      "webhooks": "write",
                      "cancel_sessions": "write",
                      "reviews": "write",
                      "dashboard": "write",
                      "insights": "write",
                      "integrations": "write",
                      "queries": "write",
                      "disputes": "write",
                      "sandbox": "write"
                    },
                    "last4": "QbM5",
                    "allowed_origins": null,
                    "token": "nc_live_EXAMPLE00000000000000000",
                    "first_used_at": null,
                    "last_used_at": null,
                    "revoked_at": null,
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "response": {
                    "object": "api_key",
                    "id": "key_wjS3tDF9X8143ppr",
                    "name": "Production server",
                    "kind": "secret",
                    "mode": "live",
                    "access": "full",
                    "permissions": {
                      "account": "write",
                      "api_keys": "write",
                      "team": "write",
                      "events": "write",
                      "customers": "write",
                      "billing": "write",
                      "stripe_connections": "write",
                      "logs": "write",
                      "flows": "write",
                      "email_domains": "write",
                      "messages": "write",
                      "webhooks": "write",
                      "cancel_sessions": "write",
                      "reviews": "write",
                      "dashboard": "write",
                      "insights": "write",
                      "integrations": "write",
                      "queries": "write",
                      "disputes": "write",
                      "sandbox": "write"
                    },
                    "last4": "QbM5",
                    "allowed_origins": null,
                    "token": "nc_live_EXAMPLE00000000000000000",
                    "first_used_at": null,
                    "last_used_at": null,
                    "revoked_at": null,
                    "created_at": "2026-11-04T10:00:00.000Z"
                  },
                  "expires_at": "2026-11-11T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/LiveRequest"
                }
              }
            }
          },
          "403": {
            "description": "a member (read only)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already decided",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/live_requests/{id}/reject": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Reject a live request (dashboard, owner or admin)",
        "tags": [
          "AI apps"
        ],
        "operationId": "rejectLiveRequest",
        "parameters": [
          {
            "name": "X-Requested-With",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "rejected: nothing runs",
            "content": {
              "application/json": {
                "example": {
                  "object": "live_request",
                  "id": "lrq_qjEB7xxu8ZAoUT4y",
                  "action": "publish_flow_live",
                  "title": "Publish a flow to live mode",
                  "summary": "Publish the current draft of Cancel flow to live mode (customers in live mode get it)",
                  "params": {
                    "flow_id": "flw_pr839pHikX4wkNpw",
                    "flow_kind": "cancel",
                    "flow_name": "Cancel flow"
                  },
                  "precondition": {
                    "kind": "draft_digest",
                    "object": "flow",
                    "id": "flw_pr839pHikX4wkNpw",
                    "expected": null,
                    "status": null,
                    "description": "The flow draft must be the one the app asked to publish."
                  },
                  "reason": "Ready for production",
                  "status": "rejected",
                  "requested_by": {
                    "type": "ai_agent",
                    "name": "Claude Code",
                    "grant_id": "ogr_cpYehACptMsaMRM6"
                  },
                  "approval_url": "http://localhost:5173/live-requests/lrq_qjEB7xxu8ZAoUT4y?account=acc_E8fCBPuUNqCUVdoq",
                  "decided_by": {
                    "id": "usr_GPLLsThB1vdVkV4j",
                    "email": "user1@example.test"
                  },
                  "decided_at": "2026-11-04T10:00:00.000Z",
                  "result": {},
                  "error": null,
                  "api_key": null,
                  "response": null,
                  "expires_at": "2026-11-11T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/LiveRequest"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/logos/{domain}": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "example": "acme.com",
          "description": "A customer's company_domain.",
          "required": true
        },
        {
          "name": "account",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          },
          "example": "acc_4fJ8sK2pQ9wLm3Xz",
          "description": "Dashboard session only: the account, in place of the NoChurn-Account header that an <img> cannot send."
        }
      ],
      "get": {
        "summary": "A customer's company logo",
        "tags": [
          "Customers"
        ],
        "operationId": "getCompanyLogo",
        "description": "The favicon of a company domain, for the customer's avatar: an image (PNG most often; never SVG), Cache-Control: private, max-age=86400. Only a domain that is the company_domain of a customer of the account answers (404 otherwise: never an open proxy). NoChurn fetches it from Google's favicon service and caches it 30 days: Google never sees who looks at it. 404 with an empty body when the domain has no logo: show your default avatar. A session matches customers of both modes; an API key, those of its mode. Resource customers (read).",
        "responses": {
          "200": {
            "description": "the logo",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "no logo (empty body), or no customer of the account has this company domain (JSON error)"
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/logs": {
      "get": {
        "summary": "List the unified log, newest first",
        "tags": [
          "Logs"
        ],
        "operationId": "listLogs",
        "description": "Stripe webhooks, your API calls, NoChurn decisions (and later the widget, outgoing webhooks and support tools), kept 90 days. Live view: poll with after=<poll_cursor> every few seconds.",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "billing,api",
            "description": "Comma-separated: stripe, api, nochurn, widget, webhook, chargebee, recurly, maxio, zuora, paddle, lemon_squeezy, polar, braintree, mollie, gocardless, razorpay, app_store, google_play, custom, intercom, crisp, zendesk, hubspot, helpscout, gorgias; billing stands for every billing tool (stripe, chargebee, recurly, maxio, zuora, paddle, lemon_squeezy, polar, braintree, mollie, gocardless, razorpay, app_store, google_play, custom)."
          },
          {
            "name": "errors",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Errors only."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A customer (cst_, cus_, your id, email), an id (log_, nevt_, iwh_) or text."
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "cst_…, cus_… or your id."
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Default: 24 hours before until. The window is 31 days at most."
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Older lines."
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only newer lines (polling)."
          }
        ],
        "responses": {
          "200": {
            "description": "live polling: nothing new keeps the cursor",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "log_entry",
                      "id": "log_FtFYHxFSjx7e96PG",
                      "livemode": false,
                      "occurred_at": "2026-11-04T09:59:00.000Z",
                      "source": "api",
                      "type": "POST /v1/customers",
                      "customer": null,
                      "summary": null,
                      "status": "500",
                      "error": true,
                      "why": null,
                      "ref": null,
                      "request_id": null
                    },
                    {
                      "object": "log_entry",
                      "id": "log_6DtqCEhQ718CVQtU",
                      "livemode": false,
                      "occurred_at": "2026-11-04T09:58:00.000Z",
                      "source": "stripe",
                      "type": "invoice.payment_failed",
                      "customer": {
                        "id": "cst_TbLNas4T3Y6wWvgN",
                        "name": null,
                        "email": "customer1@acme-corp.dev"
                      },
                      "summary": "$49.00 · insufficient_funds",
                      "status": "received",
                      "error": false,
                      "why": null,
                      "ref": null,
                      "request_id": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null,
                  "poll_cursor": "log_FtFYHxFSjx7e96PG",
                  "retention_days": 90
                },
                "schema": {
                  "$ref": "#/components/schemas/LogEntryPage"
                }
              }
            }
          },
          "400": {
            "description": "unknown source",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "restricted key without logs access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/logs/stats": {
      "get": {
        "summary": "Count of today's lines and of the errors of a window",
        "tags": [
          "Logs"
        ],
        "operationId": "getLogStats",
        "description": "today_count: today in the account's time zone, all lines. errors_count: the error lines of the window and filters passed (same params as GET /v1/logs, 24 hours by default).",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated sources, or billing (every billing tool), as GET /v1/logs."
          },
          {
            "name": "errors",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Accepted for symmetry with GET /v1/logs: errors_count only counts errors anyway."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Default: 24 hours before until. The window is 31 days at most."
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "errors of one source over 7 days",
            "content": {
              "application/json": {
                "example": {
                  "object": "log_stats",
                  "livemode": false,
                  "timezone": "Europe/Paris",
                  "today_count": 4,
                  "errors_count": 3,
                  "retention_days": 90
                },
                "schema": {
                  "$ref": "#/components/schemas/LogStats"
                }
              }
            }
          },
          "400": {
            "description": "unknown source",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/logs/{id}": {
      "get": {
        "summary": "Retrieve a line with its payload",
        "tags": [
          "Logs"
        ],
        "operationId": "getLog",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the line, payload filtered",
            "content": {
              "application/json": {
                "example": {
                  "object": "log_entry",
                  "id": "log_6NzztFYECRW8iXtw",
                  "livemode": false,
                  "occurred_at": "2026-11-04T10:00:00.000Z",
                  "source": "nochurn",
                  "type": "x.y",
                  "customer": null,
                  "summary": null,
                  "status": "ok",
                  "error": false,
                  "why": null,
                  "ref": null,
                  "request_id": null,
                  "payload": {
                    "id": "in_1",
                    "client_secret": "EXAMPLE000"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/LogEntryDetail"
                }
              }
            }
          },
          "404": {
            "description": "line of another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/messages/{id}/approve": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Approve an email waiting for review",
        "tags": [
          "Messages"
        ],
        "operationId": "approveMessage",
        "description": "It is sent at its slot in the customer's send window, or now if that slot has passed.",
        "responses": {
          "200": {
            "description": "approved",
            "content": {
              "application/json": {
                "example": {
                  "object": "message",
                  "id": "msg_jybJppHfErmGqPeU",
                  "livemode": false,
                  "channel": "email",
                  "customer": {
                    "id": "cst_a48iMM4Qwrzhi2DD",
                    "name": "Lena",
                    "email": "lena@globex.io"
                  },
                  "flow_run": null,
                  "template_key": null,
                  "locale": null,
                  "from": "acme@mail.nochurn.sh",
                  "from_name": "Acme",
                  "reply_to": "reply@mail.nochurn.sh",
                  "to": "lena@globex.io",
                  "subject": "Pay now",
                  "ai_generated": false,
                  "review_state": "approved",
                  "review_issues": [
                    "Contains a number"
                  ],
                  "reviewed_at": "2026-11-04T10:00:00Z",
                  "status": "queued",
                  "scheduled_for": "2026-11-04T10:00:00Z",
                  "schedule_reasons": [],
                  "sent_at": null,
                  "delivered_at": null,
                  "opened_at": null,
                  "clicked_at": null,
                  "bounced_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "body_text": "Pay within 3 days",
                  "body_html": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageDetail"
                }
              }
            }
          },
          "409": {
            "description": "not waiting for a review",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/messages/{id}/reject": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Reject an email: it is never sent",
        "tags": [
          "Messages"
        ],
        "operationId": "rejectMessage",
        "responses": {
          "200": {
            "description": "rejected",
            "content": {
              "application/json": {
                "example": {
                  "object": "message",
                  "id": "msg_vQ671Sb9uijrVEbZ",
                  "livemode": false,
                  "channel": "email",
                  "customer": {
                    "id": "cst_Z3cG1aZVjNGdxEqx",
                    "name": "Lena",
                    "email": "lena@globex.io"
                  },
                  "flow_run": null,
                  "template_key": null,
                  "locale": null,
                  "from": "acme@mail.nochurn.sh",
                  "from_name": "Acme",
                  "reply_to": "reply@mail.nochurn.sh",
                  "to": "lena@globex.io",
                  "subject": "Pay now",
                  "ai_generated": false,
                  "review_state": "rejected",
                  "review_issues": [
                    "Contains a number"
                  ],
                  "reviewed_at": "2026-11-04T10:00:00Z",
                  "status": "cancelled",
                  "scheduled_for": "2026-11-04T10:00:00Z",
                  "schedule_reasons": [],
                  "sent_at": null,
                  "delivered_at": null,
                  "opened_at": null,
                  "clicked_at": null,
                  "bounced_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "body_text": "Pay within 3 days",
                  "body_html": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/messages/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "summary": "Edit an email waiting for review",
        "tags": [
          "Messages"
        ],
        "operationId": "updateMessage",
        "parameters": [],
        "responses": {
          "200": {
            "description": "edited",
            "content": {
              "application/json": {
                "example": {
                  "object": "message",
                  "id": "msg_RdxeWUzh1bU5G13D",
                  "livemode": false,
                  "channel": "email",
                  "customer": {
                    "id": "cst_HP9WD9AFqWFQAYcW",
                    "name": "Lena",
                    "email": "lena@globex.io"
                  },
                  "flow_run": null,
                  "template_key": null,
                  "locale": null,
                  "from": "acme@mail.nochurn.sh",
                  "from_name": "Acme",
                  "reply_to": "reply@mail.nochurn.sh",
                  "to": "lena@globex.io",
                  "subject": "Pay now",
                  "ai_generated": false,
                  "review_state": "needs_review",
                  "review_issues": [
                    "Contains a number"
                  ],
                  "reviewed_at": null,
                  "status": "queued",
                  "scheduled_for": "2026-11-04T10:00:00Z",
                  "schedule_reasons": [],
                  "sent_at": null,
                  "delivered_at": null,
                  "opened_at": null,
                  "clicked_at": null,
                  "bounced_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "body_text": "Please update your card when you can.",
                  "body_html": "<p>Please update your card when you can.</p>"
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageDetail"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string"
                  },
                  "body_text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "body_text": "Please update your card when you can."
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "One email, with its body",
        "tags": [
          "Messages"
        ],
        "operationId": "getMessage",
        "responses": {
          "200": {
            "description": "found",
            "content": {
              "application/json": {
                "example": {
                  "object": "message",
                  "id": "msg_MVTSkdbxaUw5QLdW",
                  "livemode": false,
                  "channel": "email",
                  "customer": {
                    "id": "cst_ELRfdh1vS6cs1nLT",
                    "name": null,
                    "email": "lena@globex.io"
                  },
                  "flow_run": null,
                  "template_key": null,
                  "locale": null,
                  "from": "acme@mail.nochurn.sh",
                  "from_name": "Acme",
                  "reply_to": "reply@mail.nochurn.sh",
                  "to": "lena@globex.io",
                  "subject": "Your payment failed",
                  "ai_generated": false,
                  "review_state": "auto_approved",
                  "review_issues": [],
                  "reviewed_at": null,
                  "status": "queued",
                  "scheduled_for": "2026-11-04T10:00:00Z",
                  "schedule_reasons": [],
                  "sent_at": null,
                  "delivered_at": null,
                  "opened_at": null,
                  "clicked_at": null,
                  "bounced_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "body_text": "Hi",
                  "body_html": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/flows/{id}/send_test": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "description": "flw_… or recovery",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Send a test email to yourself",
        "tags": [
          "Flows"
        ],
        "operationId": "sendFlowTest",
        "description": "Dashboard only: emails the signed-in user, marked [TEST], with sample data, one email of the saved draft as a customer would get it (your text for the language, else the AI, else NoChurn's template). 20 a day.",
        "parameters": [
          {
            "name": "X-Requested-With",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "sent to the signed-in user",
            "content": {
              "application/json": {
                "example": {
                  "object": "flow_test_email",
                  "to": "user1@example.test",
                  "subject": "[TEST] Quick reminder about your payment",
                  "body": "Hi User,\n\nThe last payment of €49.00 for Acme didn't go through. You can update your card in one minute here: https://pay.nochurn.sh/example\n\nThanks,\nAcme",
                  "ai_generated": true,
                  "source": "ai",
                  "review": {
                    "state": "auto_approved",
                    "issues": []
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/FlowTestEmail"
                }
              }
            }
          },
          "403": {
            "description": "with an API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "step"
                ],
                "properties": {
                  "step": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "integer"
                      }
                    ],
                    "example": "email_1",
                    "description": "The email (as in the email preview: email_1…, card_removed, d30, review_follow_up…); a recovery email also by its index (0, 1, 2)."
                  },
                  "language": {
                    "type": "string",
                    "example": "fr",
                    "description": "Default: the account's language when the flow writes in it."
                  }
                }
              },
              "example": {
                "step": 1
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/messages": {
      "get": {
        "summary": "List emails sent or queued",
        "tags": [
          "Messages"
        ],
        "operationId": "listMessages",
        "parameters": [
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "flow",
            "in": "query",
            "required": false,
            "description": "flw_… or a kind (recovery…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "review_state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the customer's emails",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "message",
                      "id": "msg_7mAdYE7eDfM14gmf",
                      "livemode": false,
                      "channel": "email",
                      "customer": {
                        "id": "cst_6VxAaGbvxB6kQ3DU",
                        "name": null,
                        "email": "lena@globex.io"
                      },
                      "flow_run": null,
                      "template_key": null,
                      "locale": null,
                      "from": "acme@mail.nochurn.sh",
                      "from_name": "Acme",
                      "reply_to": "reply@mail.nochurn.sh",
                      "to": "lena@globex.io",
                      "subject": "Your payment failed",
                      "ai_generated": false,
                      "review_state": "auto_approved",
                      "review_issues": [],
                      "reviewed_at": null,
                      "status": "queued",
                      "scheduled_for": "2026-11-04T10:00:00Z",
                      "schedule_reasons": [],
                      "sent_at": null,
                      "delivered_at": null,
                      "opened_at": null,
                      "clicked_at": null,
                      "bounced_at": null,
                      "error": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/email_suppressions": {
      "get": {
        "summary": "Addresses never emailed again",
        "tags": [
          "Messages"
        ],
        "operationId": "listEmailSuppressions",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "email_suppression",
                      "id": "sup_ivHvxoZM6AmGv9EH",
                      "livemode": false,
                      "email": "x@y.com",
                      "reason": "bounced",
                      "scope": "all",
                      "message": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/EmailSuppressionList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Never email this address again",
        "tags": [
          "Messages"
        ],
        "operationId": "createEmailSuppression",
        "description": "Emails still queued to this address are cancelled.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "added",
            "content": {
              "application/json": {
                "example": {
                  "object": "email_suppression",
                  "id": "sup_TVZJJc1oa5NXcNFX",
                  "livemode": false,
                  "email": "lena@globex.io",
                  "reason": "manual",
                  "scope": "all",
                  "message": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/EmailSuppression"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "email": "Lena@globex.io"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/email_suppressions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "summary": "Email this address again",
        "tags": [
          "Messages"
        ],
        "operationId": "deleteEmailSuppression",
        "description": "Unsubscribes, spam complaints and GDPR erasures cannot be removed: only the customer can opt back in.",
        "responses": {
          "204": {
            "description": "removed"
          },
          "409": {
            "description": "an unsubscribe"
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/account/setup/share": {
      "post": {
        "summary": "Email a setup step to a colleague",
        "tags": [
          "Account"
        ],
        "operationId": "shareSetup",
        "description": "« Send to my IT team » / « Email it to a developer ». 10 a day.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "sent with the DNS records",
            "content": {
              "application/json": {
                "example": {
                  "object": "setup_share",
                  "topic": "email_domain",
                  "email": "it@acme.com",
                  "sent_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SetupShare"
                }
              }
            }
          },
          "400": {
            "description": "unknown topic",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "topic"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "topic": {
                    "type": "string",
                    "enum": [
                      "email_domain",
                      "api",
                      "cancel_modal"
                    ]
                  },
                  "note": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "email": "it@acme.com",
                "topic": "email_domain"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/oauth/grants": {
      "get": {
        "summary": "List the AI apps connected to the account",
        "tags": [
          "AI apps"
        ],
        "operationId": "listOauthGrants",
        "description": "Settings › Connected AI apps: MCP clients authorized through OAuth, with their scopes and last use.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the connected apps",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "oauth_grant",
                      "id": "ogr_ga9szsdYbQPGuhay",
                      "client": {
                        "id": "mcpc_8NeN1E6YEpZvJ7vh",
                        "name": "Cursor",
                        "verified": false,
                        "verified_name": null,
                        "client_uri": null
                      },
                      "user": {
                        "id": "usr_DgKVbPfoFB5vggGt",
                        "email": "user1@example.test",
                        "name": "User"
                      },
                      "scopes": [
                        "read",
                        "configure",
                        "sandbox"
                      ],
                      "live_read": false,
                      "live_write": "approval_required",
                      "last_used_at": null,
                      "revoked_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/OauthGrantList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/oauth/grants/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "summary": "Allow or stop an AI app's live reads",
        "tags": [
          "AI apps"
        ],
        "operationId": "updateOauthGrant",
        "description": "live_read: whether the app may read live data (NoChurn-Mode: live, reads only). Owner or admin, or the member who connected it. Turning it on needs a person in a dashboard session (never an API key or an OAuth token); turning it off works from any of them. Every change writes an Event.",
        "parameters": [
          {
            "name": "X-Requested-With",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "live reads allowed",
            "content": {
              "application/json": {
                "example": {
                  "object": "oauth_grant",
                  "id": "ogr_s9Y7BkukioSRcJSa",
                  "client": {
                    "id": "mcpc_JGFoYmVEQ9hSQq9T",
                    "name": "Cursor",
                    "verified": false,
                    "verified_name": null,
                    "client_uri": null
                  },
                  "user": {
                    "id": "usr_Znx8PDYTc71YL9kP",
                    "email": "user2@example.test",
                    "name": "User"
                  },
                  "scopes": [
                    "read",
                    "configure",
                    "sandbox"
                  ],
                  "live_read": true,
                  "live_write": "approval_required",
                  "last_used_at": null,
                  "revoked_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/OauthGrant"
                }
              }
            }
          },
          "403": {
            "description": "turning live reads on with an API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "live_read"
                ],
                "properties": {
                  "live_read": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "live_read": true
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Revoke an AI app's access",
        "tags": [
          "AI apps"
        ],
        "operationId": "revokeOauthGrant",
        "description": "Every token of the app stops working at once, and its pending live requests are cancelled (status expired, with the reason in error and a live_request.expired Event). Owner or admin, or the member who connected it.",
        "parameters": [
          {
            "name": "X-Requested-With",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "revoked",
            "content": {
              "application/json": {
                "example": {
                  "object": "oauth_grant",
                  "id": "ogr_aAN5xuUxRriy3UW2",
                  "client": {
                    "id": "mcpc_bvMfrNFty8PB8882",
                    "name": "Cursor",
                    "verified": false,
                    "verified_name": null,
                    "client_uri": null
                  },
                  "user": {
                    "id": "usr_8FSUTy7n5pyHtBp9",
                    "email": "user2@example.test",
                    "name": "User"
                  },
                  "scopes": [
                    "read",
                    "configure",
                    "sandbox"
                  ],
                  "live_read": false,
                  "live_write": "approval_required",
                  "last_used_at": null,
                  "revoked_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/OauthGrant"
                }
              }
            }
          },
          "403": {
            "description": "a member revoking someone else's app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "another account's app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/query_fields": {
      "get": {
        "summary": "What a query can read",
        "tags": [
          "Data & AI"
        ],
        "operationId": "listQueryFields",
        "description": "NoChurn's query builder: no SQL. Sources and their fields (email as domain only, sensitive attributes excluded), with a description and, for enum-like fields, their values.",
        "responses": {
          "200": {
            "description": "sources and fields",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "has_more": false,
                  "next_cursor": null,
                  "data": [
                    {
                      "source": "customers",
                      "label": "Customers",
                      "description": "The people and companies who pay you, one row per customer.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "NoChurn id",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "external_id",
                          "type": "string",
                          "label": "Your id",
                          "description": "The customer's id in your app, as you sent it."
                        },
                        {
                          "field": "email_domain",
                          "type": "string",
                          "label": "Email domain",
                          "description": "The part after @ of the customer's email (the address itself is never shown)."
                        },
                        {
                          "field": "country",
                          "type": "string",
                          "label": "Country",
                          "description": "Two-letter country code, from billing.",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "plan",
                          "type": "string",
                          "label": "Plan",
                          "description": "Name of the customer's current plan.",
                          "values": [
                            {
                              "value": "Pro",
                              "label": "Pro"
                            }
                          ],
                          "values_kind": "observed"
                        },
                        {
                          "field": "state",
                          "type": "string",
                          "label": "Billing state",
                          "description": "Where the customer stands: trialing, active, in recovery, paused, churned, or no subscription.",
                          "values": [
                            {
                              "value": "none",
                              "label": "No subscription"
                            },
                            {
                              "value": "trialing",
                              "label": "Trialing"
                            },
                            {
                              "value": "active",
                              "label": "Active"
                            },
                            {
                              "value": "in_recovery",
                              "label": "In recovery"
                            },
                            {
                              "value": "paused",
                              "label": "Paused"
                            },
                            {
                              "value": "churned",
                              "label": "Churned"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "churn_score",
                          "type": "number",
                          "label": "Churn score",
                          "description": "NoChurn's churn risk score, 0 to 100 (higher is riskier)."
                        },
                        {
                          "field": "churn_level",
                          "type": "string",
                          "label": "Churn level",
                          "description": "The churn score as a level: low, medium or high.",
                          "values": [
                            {
                              "value": "low",
                              "label": "Low"
                            },
                            {
                              "value": "medium",
                              "label": "Medium"
                            },
                            {
                              "value": "high",
                              "label": "High"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "mrr_cents",
                          "type": "number",
                          "label": "MRR (cents)",
                          "description": "Monthly recurring revenue, in cents of the customer's currency."
                        },
                        {
                          "field": "lifetime_value_cents",
                          "type": "number",
                          "label": "Lifetime value (cents)",
                          "description": "Everything the customer paid so far, in cents of their currency."
                        },
                        {
                          "field": "currency",
                          "type": "string",
                          "label": "Currency",
                          "description": "Three-letter currency code, lowercase (eur, usd…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "flagged",
                          "type": "boolean",
                          "label": "Early signal",
                          "description": "An early churn signal is raised on the customer."
                        },
                        {
                          "field": "created_at",
                          "type": "date",
                          "label": "Created",
                          "description": "When the customer was created."
                        },
                        {
                          "field": "churned_at",
                          "type": "date",
                          "label": "Churned",
                          "description": "When the customer churned (empty if they did not)."
                        },
                        {
                          "field": "last_seen_at",
                          "type": "date",
                          "label": "Last seen",
                          "description": "Last activity NoChurn knows of in your product."
                        },
                        {
                          "field": "utm_source",
                          "type": "string",
                          "label": "UTM source",
                          "description": "Where the customer came from (utm_source at signup: google, facebook, newsletter…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "utm_medium",
                          "type": "string",
                          "label": "UTM medium",
                          "description": "The marketing channel at signup (utm_medium: cpc, email, social…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "utm_campaign",
                          "type": "string",
                          "label": "UTM campaign",
                          "description": "The campaign the customer signed up from (utm_campaign).",
                          "values": [],
                          "values_kind": "observed"
                        }
                      ]
                    },
                    {
                      "source": "subscriptions",
                      "label": "Subscriptions",
                      "description": "Each plan a customer subscribes to, with its status and amount.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "NoChurn id",
                          "description": "NoChurn's id of the row."
                        },
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "status",
                          "type": "string",
                          "label": "Status",
                          "description": "The billing status: active, trialing, past due, canceled…",
                          "values": [
                            {
                              "value": "incomplete",
                              "label": "Incomplete"
                            },
                            {
                              "value": "incomplete_expired",
                              "label": "Incomplete (expired)"
                            },
                            {
                              "value": "trialing",
                              "label": "Trialing"
                            },
                            {
                              "value": "active",
                              "label": "Active"
                            },
                            {
                              "value": "past_due",
                              "label": "Past due"
                            },
                            {
                              "value": "canceled",
                              "label": "Canceled"
                            },
                            {
                              "value": "unpaid",
                              "label": "Unpaid"
                            },
                            {
                              "value": "paused",
                              "label": "Paused"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "plan",
                          "type": "string",
                          "label": "Plan",
                          "description": "Name of the plan.",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "interval",
                          "type": "string",
                          "label": "Interval",
                          "description": "How often it bills (month, year…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "amount_cents",
                          "type": "number",
                          "label": "Amount (cents)",
                          "description": "Price per interval, in cents of its currency."
                        },
                        {
                          "field": "currency",
                          "type": "string",
                          "label": "Currency",
                          "description": "Three-letter currency code, lowercase (eur, usd…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "cancel_at_period_end",
                          "type": "boolean",
                          "label": "Cancels at period end",
                          "description": "Canceled, but runs until the end of the paid period."
                        },
                        {
                          "field": "started_at",
                          "type": "date",
                          "label": "Started",
                          "description": "When the subscription started."
                        },
                        {
                          "field": "canceled_at",
                          "type": "date",
                          "label": "Canceled",
                          "description": "When it was canceled."
                        },
                        {
                          "field": "ended_at",
                          "type": "date",
                          "label": "Ended",
                          "description": "When it actually stopped."
                        },
                        {
                          "field": "created_at",
                          "type": "date",
                          "label": "Created",
                          "description": "When it was created."
                        }
                      ]
                    },
                    {
                      "source": "invoices",
                      "label": "Invoices",
                      "description": "Bills sent to customers, with what was due, what was paid and the last decline code.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "NoChurn id",
                          "description": "NoChurn's id of the row."
                        },
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "status",
                          "type": "string",
                          "label": "Status",
                          "description": "draft, open (not paid yet), paid, uncollectible or void.",
                          "values": [
                            {
                              "value": "draft",
                              "label": "Draft"
                            },
                            {
                              "value": "open",
                              "label": "Open"
                            },
                            {
                              "value": "paid",
                              "label": "Paid"
                            },
                            {
                              "value": "uncollectible",
                              "label": "Uncollectible"
                            },
                            {
                              "value": "void",
                              "label": "Void"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "amount_due_cents",
                          "type": "number",
                          "label": "Amount due (cents)",
                          "description": "What the customer owes, in cents."
                        },
                        {
                          "field": "amount_paid_cents",
                          "type": "number",
                          "label": "Amount paid (cents)",
                          "description": "What was paid, in cents."
                        },
                        {
                          "field": "currency",
                          "type": "string",
                          "label": "Currency",
                          "description": "Three-letter currency code, lowercase (eur, usd…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "decline_code",
                          "type": "string",
                          "label": "Decline code",
                          "description": "Why the last payment of the invoice was refused (insufficient_funds…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "attempt_count",
                          "type": "number",
                          "label": "Attempts",
                          "description": "How many times payment was attempted."
                        },
                        {
                          "field": "created_at",
                          "type": "date",
                          "label": "Created",
                          "description": "When the invoice was created."
                        }
                      ]
                    },
                    {
                      "source": "payments",
                      "label": "Payments",
                      "description": "Each payment attempt (a charge), succeeded or failed, with its decline code.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "NoChurn id",
                          "description": "NoChurn's id of the row."
                        },
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "status",
                          "type": "string",
                          "label": "Status",
                          "description": "succeeded, failed or pending.",
                          "values": [
                            {
                              "value": "succeeded",
                              "label": "Succeeded"
                            },
                            {
                              "value": "failed",
                              "label": "Failed"
                            },
                            {
                              "value": "pending",
                              "label": "Pending"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "amount_cents",
                          "type": "number",
                          "label": "Amount (cents)",
                          "description": "Amount of the attempt, in cents."
                        },
                        {
                          "field": "amount_refunded_cents",
                          "type": "number",
                          "label": "Refunded (cents)",
                          "description": "Amount refunded, in cents."
                        },
                        {
                          "field": "currency",
                          "type": "string",
                          "label": "Currency",
                          "description": "Three-letter currency code, lowercase (eur, usd…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "decline_code",
                          "type": "string",
                          "label": "Decline code",
                          "description": "Why the bank or the payment provider refused this attempt.",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "occurred_at",
                          "type": "date",
                          "label": "Date",
                          "description": "When the payment was attempted."
                        }
                      ]
                    },
                    {
                      "source": "cancel_sessions",
                      "label": "Cancel sessions",
                      "description": "Each time a customer opened the cancel flow, with the reason they chose and what happened.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "Session",
                          "description": "NoChurn id of the cancel session."
                        },
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "reason",
                          "type": "string",
                          "label": "Reason",
                          "description": "The cancel reason the customer chose (its key in your cancel flow).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "outcome",
                          "type": "string",
                          "label": "Outcome",
                          "description": "saved (they stayed), cancelled, or abandoned (closed without deciding).",
                          "values": [
                            {
                              "value": "saved",
                              "label": "Saved"
                            },
                            {
                              "value": "cancelled",
                              "label": "Cancelled"
                            },
                            {
                              "value": "abandoned",
                              "label": "Abandoned"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "offer_accepted",
                          "type": "boolean",
                          "label": "Offer accepted",
                          "description": "The customer took the offer."
                        },
                        {
                          "field": "source",
                          "type": "string",
                          "label": "Source",
                          "description": "Where the cancel flow was opened: widget, hosted page or API.",
                          "values": [
                            {
                              "value": "widget",
                              "label": "Widget"
                            },
                            {
                              "value": "hosted",
                              "label": "Hosted"
                            },
                            {
                              "value": "api",
                              "label": "API"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "saved_mrr_cents",
                          "type": "number",
                          "label": "MRR saved (cents)",
                          "description": "MRR kept when the customer stayed, in cents."
                        },
                        {
                          "field": "currency",
                          "type": "string",
                          "label": "Currency",
                          "description": "Currency of the subscription (or of the customer).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "created_at",
                          "type": "date",
                          "label": "Date",
                          "description": "When the cancel flow was opened."
                        }
                      ]
                    },
                    {
                      "source": "flow_runs",
                      "label": "Flow runs",
                      "description": "Each time a NoChurn flow (prevention, recovery, cancel, win-back) ran for a customer, and its outcome.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "Run",
                          "description": "NoChurn id of the run."
                        },
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "flow",
                          "type": "string",
                          "label": "Flow",
                          "description": "Which flow: prevention, recovery, cancel or win-back.",
                          "values": [
                            {
                              "value": "prevention",
                              "label": "Prevention"
                            },
                            {
                              "value": "recovery",
                              "label": "Recovery"
                            },
                            {
                              "value": "cancel",
                              "label": "Cancel"
                            },
                            {
                              "value": "winback",
                              "label": "Win-back"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "state",
                          "type": "string",
                          "label": "State",
                          "description": "active (still running), succeeded, failed or stopped.",
                          "values": [
                            {
                              "value": "active",
                              "label": "Active"
                            },
                            {
                              "value": "succeeded",
                              "label": "Succeeded"
                            },
                            {
                              "value": "failed",
                              "label": "Failed"
                            },
                            {
                              "value": "stopped",
                              "label": "Stopped"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "outcome",
                          "type": "string",
                          "label": "Outcome",
                          "description": "What the run achieved: prevented, recovered, saved, won back, lost…",
                          "values": [
                            {
                              "value": "prevented",
                              "label": "Prevented"
                            },
                            {
                              "value": "recovered",
                              "label": "Recovered"
                            },
                            {
                              "value": "saved",
                              "label": "Saved"
                            },
                            {
                              "value": "won_back",
                              "label": "Won back"
                            },
                            {
                              "value": "lost",
                              "label": "Lost"
                            },
                            {
                              "value": "cancelled",
                              "label": "Cancelled"
                            },
                            {
                              "value": "abandoned",
                              "label": "Abandoned"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "confirmation",
                          "type": "string",
                          "label": "Confirmation",
                          "description": "Whether the outcome held for 30 days: pending, confirmed or reverted.",
                          "values": [
                            {
                              "value": "pending",
                              "label": "Pending"
                            },
                            {
                              "value": "confirmed",
                              "label": "Confirmed"
                            },
                            {
                              "value": "reverted",
                              "label": "Reverted"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "holdout",
                          "type": "boolean",
                          "label": "Holdout",
                          "description": "In the control group (NoChurn did nothing, to measure the lift)."
                        },
                        {
                          "field": "outcome_cents",
                          "type": "number",
                          "label": "Amount (cents)",
                          "description": "Amount recovered, in cents."
                        },
                        {
                          "field": "outcome_mrr_cents",
                          "type": "number",
                          "label": "MRR (cents)",
                          "description": "MRR kept or won back, in cents."
                        },
                        {
                          "field": "currency",
                          "type": "string",
                          "label": "Currency",
                          "description": "Three-letter currency code, lowercase (eur, usd…).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "started_at",
                          "type": "date",
                          "label": "Started",
                          "description": "When the run started."
                        },
                        {
                          "field": "outcome_at",
                          "type": "date",
                          "label": "Outcome date",
                          "description": "When the outcome was reached."
                        }
                      ]
                    },
                    {
                      "source": "usage_events",
                      "label": "Usage events",
                      "description": "What customers do in your product, as sent to NoChurn (events and page views).",
                      "fields": [
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "name",
                          "type": "string",
                          "label": "Event",
                          "description": "Name of the event (page_view, or what you send with track).",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "occurred_at",
                          "type": "date",
                          "label": "Date",
                          "description": "When it happened."
                        }
                      ]
                    },
                    {
                      "source": "messages",
                      "label": "Emails",
                      "description": "The emails NoChurn sent to customers, and whether they were opened or clicked.",
                      "fields": [
                        {
                          "field": "id",
                          "type": "string",
                          "label": "Email",
                          "description": "NoChurn id of the email."
                        },
                        {
                          "field": "customer",
                          "type": "string",
                          "label": "Customer",
                          "description": "NoChurn id of the customer (cst_…)."
                        },
                        {
                          "field": "template",
                          "type": "string",
                          "label": "Template",
                          "description": "Which email of the flow it was.",
                          "values": [],
                          "values_kind": "observed"
                        },
                        {
                          "field": "status",
                          "type": "string",
                          "label": "Status",
                          "description": "queued, sent, delivered, bounced, failed…",
                          "values": [
                            {
                              "value": "queued",
                              "label": "Queued"
                            },
                            {
                              "value": "sending",
                              "label": "Sending"
                            },
                            {
                              "value": "sent",
                              "label": "Sent"
                            },
                            {
                              "value": "delivered",
                              "label": "Delivered"
                            },
                            {
                              "value": "bounced",
                              "label": "Bounced"
                            },
                            {
                              "value": "complained",
                              "label": "Complained"
                            },
                            {
                              "value": "failed",
                              "label": "Failed"
                            },
                            {
                              "value": "suppressed",
                              "label": "Suppressed"
                            },
                            {
                              "value": "cancelled",
                              "label": "Cancelled"
                            }
                          ],
                          "values_kind": "closed"
                        },
                        {
                          "field": "opened",
                          "type": "boolean",
                          "label": "Opened",
                          "description": "The customer opened it."
                        },
                        {
                          "field": "clicked",
                          "type": "boolean",
                          "label": "Clicked",
                          "description": "The customer clicked a link in it."
                        },
                        {
                          "field": "ai_generated",
                          "type": "boolean",
                          "label": "Written by AI",
                          "description": "The text was written by NoChurn's AI."
                        },
                        {
                          "field": "sent_at",
                          "type": "date",
                          "label": "Sent",
                          "description": "When it was sent."
                        }
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/QuerySourceList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/queries/run": {
      "post": {
        "summary": "Run a query",
        "tags": [
          "Data & AI"
        ],
        "operationId": "runQuery",
        "description": "Read only, 5 s at most, 1,000 rows, 60 runs an hour. Each run is recorded.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "columns and rows",
            "content": {
              "application/json": {
                "example": {
                  "object": "query_run",
                  "id": "qrn_QWkMX2B2yQAhTU1K",
                  "status": "succeeded",
                  "spec": {
                    "source": "customers",
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [
                      {
                        "field": "plan"
                      }
                    ],
                    "fields": [],
                    "filters": [],
                    "period": null,
                    "sort": null,
                    "limit": 100
                  },
                  "columns": [
                    "plan",
                    "count"
                  ],
                  "rows": [
                    [
                      "Pro",
                      1
                    ]
                  ],
                  "row_count": 1,
                  "duration_ms": 42,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/QueryRun"
                }
              }
            }
          },
          "400": {
            "description": "unknown field",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "spec": {
                    "$ref": "#/components/schemas/QuerySpec"
                  },
                  "saved_query": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "spec": {
                  "source": "customers",
                  "metrics": [
                    {
                      "fn": "count"
                    }
                  ],
                  "dimensions": [
                    {
                      "field": "plan"
                    }
                  ]
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/queries/translate": {
      "post": {
        "summary": "Ask anything",
        "tags": [
          "Data & AI"
        ],
        "operationId": "translateQuery",
        "description": "The AI turns the question into a query spec from the catalogue only (never rows); the same validator applies.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "a spec to run",
            "content": {
              "application/json": {
                "example": {
                  "object": "query_translation",
                  "question": "How many customers?",
                  "spec": {
                    "source": "customers",
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [],
                    "fields": [],
                    "filters": [],
                    "period": null,
                    "sort": null,
                    "limit": 100
                  },
                  "explanation": "Count."
                },
                "schema": {
                  "$ref": "#/components/schemas/QueryTranslation"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "question"
                ],
                "properties": {
                  "question": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "question": "How many customers?"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/saved_queries": {
      "post": {
        "summary": "Save a query",
        "tags": [
          "Data & AI"
        ],
        "operationId": "createSavedQuery",
        "parameters": [],
        "responses": {
          "201": {
            "description": "saved",
            "content": {
              "application/json": {
                "example": {
                  "object": "saved_query",
                  "id": "qry_b6R81JjTFD5ZJFpL",
                  "name": "Customers by plan",
                  "question": null,
                  "spec": {
                    "source": "customers",
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [
                      {
                        "field": "plan"
                      }
                    ],
                    "fields": [],
                    "filters": [],
                    "period": null,
                    "sort": null,
                    "limit": 100
                  },
                  "view": "chart",
                  "chart": {},
                  "created_at": "2026-11-04T10:00:00Z",
                  "schedule": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SavedQuery"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "spec"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "question": {
                    "type": "string"
                  },
                  "spec": {
                    "$ref": "#/components/schemas/QuerySpec"
                  },
                  "view": {
                    "type": "string",
                    "enum": [
                      "table",
                      "chart"
                    ]
                  },
                  "chart": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "name": "Customers by plan",
                "spec": {
                  "source": "customers",
                  "metrics": [
                    {
                      "fn": "count"
                    }
                  ],
                  "dimensions": [
                    {
                      "field": "plan"
                    }
                  ]
                },
                "view": "chart"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "Saved queries",
        "tags": [
          "Data & AI"
        ],
        "operationId": "listSavedQueries",
        "responses": {
          "200": {
            "description": "newest first",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SavedQueryList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/saved_queries/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Rename or change a saved query",
        "tags": [
          "Data & AI"
        ],
        "operationId": "updateSavedQuery",
        "parameters": [],
        "responses": {
          "200": {
            "description": "updated",
            "content": {
              "application/json": {
                "example": {
                  "object": "saved_query",
                  "id": "qry_gpKxkTzgkCTFJzo6",
                  "name": "Renamed",
                  "question": null,
                  "spec": {
                    "sort": null,
                    "limit": 100,
                    "fields": [],
                    "period": null,
                    "source": "customers",
                    "filters": [],
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [
                      {
                        "field": "plan"
                      }
                    ]
                  },
                  "view": "table",
                  "chart": {},
                  "created_at": "2026-11-04T10:00:00Z",
                  "schedule": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SavedQuery"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "question": {
                    "type": "string"
                  },
                  "spec": {
                    "$ref": "#/components/schemas/QuerySpec"
                  },
                  "view": {
                    "type": "string",
                    "enum": [
                      "table",
                      "chart"
                    ]
                  },
                  "chart": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "name": "Renamed"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Delete a saved query (and its schedule)",
        "tags": [
          "Data & AI"
        ],
        "operationId": "deleteSavedQuery",
        "responses": {
          "200": {
            "description": "deleted",
            "content": {
              "application/json": {
                "example": {
                  "object": "saved_query",
                  "id": "qry_A7F5EsvVczn9SwwU",
                  "name": "Q",
                  "question": null,
                  "spec": {
                    "sort": null,
                    "limit": 100,
                    "fields": [],
                    "period": null,
                    "source": "customers",
                    "filters": [],
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [
                      {
                        "field": "plan"
                      }
                    ]
                  },
                  "view": "table",
                  "chart": {},
                  "created_at": "2026-11-04T10:00:00Z",
                  "schedule": null,
                  "deleted": true
                },
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "deleted"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/saved_queries/{id}/schedule": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Schedule a report",
        "tags": [
          "Data & AI"
        ],
        "operationId": "scheduleSavedQuery",
        "parameters": [],
        "responses": {
          "200": {
            "description": "scheduled",
            "content": {
              "application/json": {
                "example": {
                  "object": "saved_query",
                  "id": "qry_tDhxTamfnwgkYVK4",
                  "name": "Q",
                  "question": null,
                  "spec": {
                    "sort": null,
                    "limit": 100,
                    "fields": [],
                    "period": null,
                    "source": "customers",
                    "filters": [],
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [
                      {
                        "field": "plan"
                      }
                    ]
                  },
                  "view": "table",
                  "chart": {},
                  "created_at": "2026-11-04T10:00:00Z",
                  "schedule": {
                    "cadence": "weekly",
                    "day": "thursday",
                    "time": "11:00",
                    "timezone": "Europe/Paris",
                    "destination": "email",
                    "target": null,
                    "next_run_at": "2026-11-05T10:00:00Z",
                    "last_run": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/SavedQuery"
                }
              }
            }
          },
          "400": {
            "description": "reports only go to the account's owners and admins",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cadence",
                  "destination"
                ],
                "properties": {
                  "cadence": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "weekly",
                      "monthly"
                    ]
                  },
                  "day": {
                    "oneOf": [
                      {
                        "type": "string",
                        "enum": [
                          "monday",
                          "tuesday",
                          "wednesday",
                          "thursday",
                          "friday",
                          "saturday",
                          "sunday"
                        ]
                      },
                      {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 28
                      }
                    ],
                    "description": "weekly: monday…sunday (monday by default); monthly: 1–28 (1 by default); daily: leave out."
                  },
                  "destination": {
                    "type": "string",
                    "enum": [
                      "email",
                      "slack"
                    ]
                  },
                  "target": {
                    "type": "string",
                    "description": "Email address (default: the account's admins)."
                  }
                }
              },
              "example": {
                "cadence": "weekly",
                "day": "thursday",
                "destination": "email"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Stop a report",
        "tags": [
          "Data & AI"
        ],
        "operationId": "unscheduleSavedQuery",
        "responses": {
          "200": {
            "description": "no schedule",
            "content": {
              "application/json": {
                "example": {
                  "object": "saved_query",
                  "id": "qry_1qvCv4aVywRRALhc",
                  "name": "Q",
                  "question": null,
                  "spec": {
                    "sort": null,
                    "limit": 100,
                    "fields": [],
                    "period": null,
                    "source": "customers",
                    "filters": [],
                    "metrics": [
                      {
                        "fn": "count"
                      }
                    ],
                    "dimensions": [
                      {
                        "field": "plan"
                      }
                    ]
                  },
                  "view": "table",
                  "chart": {},
                  "created_at": "2026-11-04T10:00:00Z",
                  "schedule": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SavedQuery"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/replies": {
      "get": {
        "summary": "Replies to NoChurn emails (To do)",
        "tags": [
          "Messages"
        ],
        "operationId": "listReplies",
        "description": "Sorted by the AI (or keyword rules). `text` is the customer's: display it as text.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "handled",
                "ignored"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "wants_to_return",
                "question",
                "not_interested",
                "unsubscribe",
                "out_of_office",
                "other"
              ]
            }
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "newest first, with the count still open",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "message_reply",
                      "id": "rpl_xW5HbqtzKvVVfCqt",
                      "message": "msg_LS2QZwJVaaT8F3Ah",
                      "customer": "cst_Fm54HKouWUM3Uyyq",
                      "customer_email": "lena@globex.io",
                      "customer_name": "Lena",
                      "flow_run": null,
                      "flow": null,
                      "from": "lena@globex.io",
                      "subject": "Re: We miss you",
                      "text": "Do you have a cheaper plan?",
                      "category": "question",
                      "summary": null,
                      "needs_human": true,
                      "status": "open",
                      "handled_at": null,
                      "answer_message": null,
                      "received_at": "2026-11-04T10:00:00Z",
                      "livemode": false
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null,
                  "open_count": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageReplyPage"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/replies/{id}/ignore": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Ignore a reply",
        "tags": [
          "Messages"
        ],
        "operationId": "ignoreReply",
        "responses": {
          "200": {
            "description": "closed",
            "content": {
              "application/json": {
                "example": {
                  "object": "message_reply",
                  "id": "rpl_5wxJp3DCg4vPhrHg",
                  "message": "msg_vFR7ofzoUzQuc5aW",
                  "customer": "cst_dEdhnVf8D1pgPcDU",
                  "customer_email": "lena@globex.io",
                  "customer_name": "Lena",
                  "flow_run": null,
                  "flow": null,
                  "from": "lena@globex.io",
                  "subject": "Re: We miss you",
                  "text": "Do you have a cheaper plan?",
                  "category": "question",
                  "summary": null,
                  "needs_human": true,
                  "status": "ignored",
                  "handled_at": "2026-11-04T10:00:00Z",
                  "answer_message": null,
                  "received_at": "2026-11-04T10:00:00Z",
                  "livemode": false
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageReply"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/replies/{id}/handle": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Mark a reply handled",
        "tags": [
          "Messages"
        ],
        "operationId": "handleReply",
        "responses": {
          "200": {
            "description": "closed",
            "content": {
              "application/json": {
                "example": {
                  "object": "message_reply",
                  "id": "rpl_J7ZecZZmYhhFcKHF",
                  "message": "msg_MwzQ5iciTQXVwc3Q",
                  "customer": "cst_SyJ5a4KUH3XNYBMZ",
                  "customer_email": "lena@globex.io",
                  "customer_name": "Lena",
                  "flow_run": null,
                  "flow": null,
                  "from": "lena@globex.io",
                  "subject": "Re: We miss you",
                  "text": "Do you have a cheaper plan?",
                  "category": "question",
                  "summary": null,
                  "needs_human": true,
                  "status": "handled",
                  "handled_at": "2026-11-04T10:00:00Z",
                  "answer_message": null,
                  "received_at": "2026-11-04T10:00:00Z",
                  "livemode": false
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageReply"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/messages/{id}/reply": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Answer the customer",
        "tags": [
          "Messages"
        ],
        "operationId": "replyToMessage",
        "description": "Sent now (it answers the customer: no send window), from the same sender, \"Re:\" the original subject. The open replies to this email are marked handled.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "queued to send now",
            "content": {
              "application/json": {
                "example": {
                  "object": "message",
                  "id": "msg_kevJoAGQTaFsNwmx",
                  "livemode": false,
                  "channel": "email",
                  "customer": {
                    "id": "cst_dsRbczhJGFgyjPTc",
                    "name": "Lena",
                    "email": "lena@globex.io"
                  },
                  "flow_run": null,
                  "template_key": "reply.human",
                  "locale": null,
                  "from": "account1@mail.nochurn.sh",
                  "from_name": "Account 1",
                  "reply_to": "reply@mail.nochurn.sh",
                  "to": "lena@globex.io",
                  "subject": "Re: We miss you",
                  "ai_generated": false,
                  "review_state": "auto_approved",
                  "review_issues": [],
                  "reviewed_at": null,
                  "status": "queued",
                  "scheduled_for": "2026-11-04T10:00:00Z",
                  "schedule_reasons": [],
                  "sent_at": null,
                  "delivered_at": null,
                  "opened_at": null,
                  "clicked_at": null,
                  "bounced_at": null,
                  "error": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "body_text": "Yes: the Starter plan is 19 €/month.",
                  "body_html": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MessageDetail"
                }
              }
            }
          },
          "400": {
            "description": "empty text",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "text": "Yes: the Starter plan is 19 €/month."
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/review_platforms": {
      "get": {
        "summary": "Your review pages",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "listReviewPlatforms",
        "responses": {
          "200": {
            "description": "the list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "review_platform",
                      "id": "rvp_DLEKX7d6fQenDkjn",
                      "kind": "g2",
                      "url": "https://g2.com/acme",
                      "status": "active",
                      "review_count": null,
                      "rating": null,
                      "rules": {},
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ReviewPlatformList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Add a review page",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "createReviewPlatform",
        "description": "Happy leavers are asked once, ever, on the platform with the fewest NoChurn requests this quarter. No reward is ever offered.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "added",
            "content": {
              "application/json": {
                "example": {
                  "object": "review_platform",
                  "id": "rvp_se7mSPq3Ww8gp51o",
                  "kind": "trustpilot",
                  "url": "https://trustpilot.com/review/acme.com",
                  "status": "active",
                  "review_count": null,
                  "rating": null,
                  "rules": {
                    "countries": [
                      "FR"
                    ]
                  },
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ReviewPlatform"
                }
              }
            }
          },
          "422": {
            "description": "not an https link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "url"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "g2",
                      "capterra",
                      "trustpilot",
                      "google",
                      "app_store",
                      "product_hunt"
                    ]
                  },
                  "url": {
                    "type": "string"
                  },
                  "review_count": {
                    "type": "integer"
                  },
                  "rating": {
                    "type": "number"
                  },
                  "rules": {
                    "type": "object",
                    "properties": {
                      "countries": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "plans": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "kind": "trustpilot",
                "url": "https://trustpilot.com/review/acme.com",
                "rules": {
                  "countries": [
                    "FR"
                  ]
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/review_requests/stats": {
      "get": {
        "summary": "Review requests this month",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "getReviewRequestStats",
        "responses": {
          "200": {
            "description": "counts from what NoChurn did",
            "content": {
              "application/json": {
                "example": {
                  "object": "review_request_stats",
                  "period_start": "2026-11-01T00:00:00Z",
                  "cancellations": 0,
                  "requested": 0,
                  "clicked": 0,
                  "follow_ups_sent": 0,
                  "by_platform": {}
                },
                "schema": {
                  "$ref": "#/components/schemas/ReviewRequestStats"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/score": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "description": "cst_…, cus_… or your own id.",
          "required": true
        }
      ],
      "get": {
        "summary": "Churn score of a customer",
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomerScore",
        "description": "Heuristic v1 (0–100) with the factors that explain it, the prevention flag, and the last 30 computations.",
        "responses": {
          "200": {
            "description": "the score",
            "content": {
              "application/json": {
                "example": {
                  "object": "churn_score",
                  "customer": "cst_hnAz8xVcGi19CcbK",
                  "score": 10,
                  "level": "low",
                  "model_version": "heuristic-v1",
                  "computed_at": "2026-11-04T10:00:00Z",
                  "factors": [
                    {
                      "key": "base",
                      "label": "Starting point",
                      "points": 10
                    }
                  ],
                  "flag": null,
                  "history": [
                    {
                      "score": 10,
                      "level": "low",
                      "trigger": "manual",
                      "computed_at": "2026-11-04T10:00:00Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/ChurnScore"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/customers/{id}/attributes": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Merge custom attributes",
        "tags": [
          "Customers"
        ],
        "operationId": "mergeCustomerAttributes",
        "description": "The type of a key is inferred at its first send and locked. null removes the key. 100 keys per account, snake_case, texts of 255 characters at most.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "merged",
            "content": {
              "application/json": {
                "example": {
                  "object": "customer",
                  "id": "cst_kWvFWUJMzhonPp9G",
                  "stripe_customer_id": null,
                  "external_id": "u_1",
                  "stripe_account": null,
                  "livemode": false,
                  "email": "ana@acme.com",
                  "company_domain": "acme.com",
                  "name": null,
                  "locale": null,
                  "country": null,
                  "region": null,
                  "timezone": "Europe/Paris",
                  "timezone_source": "account",
                  "attributes": {
                    "plan": "pro",
                    "seats": 12,
                    "beta": true
                  },
                  "churn_score": null,
                  "churn_level": null,
                  "flag": null,
                  "state": "none",
                  "plan": null,
                  "mrr_cents": 0,
                  "lifetime_value_cents": 0,
                  "currency": null,
                  "payment_method": null,
                  "last_seen_at": null,
                  "churned_at": null,
                  "flows_paused_at": null,
                  "signup_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z",
                  "updated_at": "2026-11-04T10:00:00.000Z",
                  "identifiers": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "422": {
            "description": "wrong type for a locked attribute: nothing is saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "attributes"
                ],
                "properties": {
                  "attributes": {
                    "type": "object",
                    "additionalProperties": true,
                    "example": {
                      "plan": "pro",
                      "seats": 12
                    }
                  }
                }
              },
              "example": {
                "attributes": {
                  "plan": "pro",
                  "seats": 12,
                  "beta": true
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/attribute_definitions": {
      "get": {
        "summary": "Custom attributes of the account",
        "tags": [
          "Data & AI"
        ],
        "operationId": "listAttributeDefinitions",
        "responses": {
          "200": {
            "description": "with their type and coverage",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "has_more": false,
                  "next_cursor": null,
                  "data": [
                    {
                      "object": "attribute_definition",
                      "id": "atd_rtVRQsuCTXLR8ka2",
                      "key": "plan",
                      "type": "string",
                      "sensitive": false,
                      "customers_count": 1,
                      "coverage": 1,
                      "last_seen_at": "2026-11-04T10:00:00Z",
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/AttributeDefinitionList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Declare an attribute before its first send",
        "tags": [
          "Data & AI"
        ],
        "operationId": "createAttributeDefinition",
        "description": "Locks the type from the start (same rules as the first send: snake_case key, 100 per account and mode). Declaring it again with the same type answers 200; another type is attribute_type_mismatch.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "declared",
            "content": {
              "application/json": {
                "example": {
                  "object": "attribute_definition",
                  "id": "atd_7donEcLpikLuJtpn",
                  "key": "seats",
                  "type": "number",
                  "sensitive": false,
                  "customers_count": 0,
                  "coverage": 0,
                  "last_seen_at": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/AttributeDefinition"
                }
              }
            }
          },
          "422": {
            "description": "already declared with another type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key",
                  "type"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "example": "seats"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "string",
                      "number",
                      "boolean",
                      "date"
                    ]
                  },
                  "sensitive": {
                    "type": "boolean",
                    "description": "Never sent to the AI."
                  }
                }
              },
              "example": {
                "key": "seats",
                "type": "number"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/attribute_definitions/{key}": {
      "parameters": [
        {
          "name": "key",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Mark an attribute sensitive",
        "tags": [
          "Data & AI"
        ],
        "operationId": "updateAttributeDefinition",
        "parameters": [],
        "responses": {
          "200": {
            "description": "updated",
            "content": {
              "application/json": {
                "example": {
                  "object": "attribute_definition",
                  "id": "atd_6EfKZkNvSS7YEkpS",
                  "key": "plan",
                  "type": "string",
                  "sensitive": true,
                  "customers_count": 1,
                  "coverage": 1,
                  "last_seen_at": "2026-11-04T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/AttributeDefinition"
                }
              }
            }
          },
          "404": {
            "description": "unknown attribute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sensitive"
                ],
                "properties": {
                  "sensitive": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "sensitive": true
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/imports": {
      "post": {
        "summary": "Import a CSV of customers and attributes",
        "tags": [
          "Data & AI"
        ],
        "operationId": "createImport",
        "description": "Send the file as multipart `file`, or its text as `csv`. Columns are mapped by name (customer, external_id, email, stripe_customer, name, country, locale; anything else → attribute:<snake_case>), override with `mapping`. dry_run returns the mapping and the first rows. A row with an unknown customer and an external_id or email creates it.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "dry run: proposed mapping and preview",
            "content": {
              "application/json": {
                "example": {
                  "object": "import_preview",
                  "columns": [
                    {
                      "name": "User ID",
                      "target": "external_id"
                    },
                    {
                      "name": "Email",
                      "target": "email"
                    },
                    {
                      "name": "Plan",
                      "target": "attribute:plan"
                    }
                  ],
                  "rows_total": 1,
                  "rows": [
                    {
                      "User ID": "u_1",
                      "Email": "ana@example.test",
                      "Plan": "pro"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/ImportPreview"
                }
              }
            }
          },
          "202": {
            "description": "queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "import",
                  "id": "imp_HGApj7P3iZ2chfFT",
                  "status": "pending",
                  "filename": "c.csv",
                  "mapping": {
                    "user_id": "external_id",
                    "plan": "attribute:plan",
                    "seats": "attribute:seats"
                  },
                  "rows_total": 3,
                  "rows_imported": 0,
                  "rows_failed": 0,
                  "errors": [],
                  "livemode": false,
                  "created_at": "2026-11-04T10:00:00Z",
                  "started_at": null,
                  "finished_at": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Import"
                }
              }
            }
          },
          "400": {
            "description": "no column identifies the customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "csv": {
                    "type": "string"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "dry_run": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "csv": "User ID,Email,Plan\nu_1,ana@example.test,pro\n",
                "dry_run": true
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "csv": {
                    "type": "string"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "dry_run": {
                    "type": "boolean"
                  }
                }
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "Imports",
        "tags": [
          "Data & AI"
        ],
        "operationId": "listImports",
        "responses": {
          "200": {
            "description": "newest first",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ImportList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/imports/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "get": {
        "summary": "An import and its error report",
        "tags": [
          "Data & AI"
        ],
        "operationId": "getImport",
        "responses": {
          "200": {
            "description": "the import",
            "content": {
              "application/json": {
                "example": {
                  "object": "import",
                  "id": "imp_T6rNbRAuJxkNV4gX",
                  "status": "pending",
                  "filename": null,
                  "mapping": {
                    "email": "email"
                  },
                  "rows_total": 1,
                  "rows_imported": 0,
                  "rows_failed": 0,
                  "errors": [],
                  "livemode": false,
                  "created_at": "2026-11-04T10:00:00Z",
                  "started_at": null,
                  "finished_at": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Import"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/segment_fields": {
      "get": {
        "summary": "Fields a segment can use",
        "tags": [
          "Flows"
        ],
        "operationId": "listSegmentFields",
        "responses": {
          "200": {
            "description": "by source (stripe, nochurn, custom)",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "has_more": false,
                  "next_cursor": null,
                  "data": [
                    {
                      "source": "nochurn",
                      "field": "churn_score",
                      "type": "number",
                      "label": "Churn score (0–100)",
                      "ops": [
                        "eq",
                        "neq",
                        "gt",
                        "gte",
                        "lt",
                        "lte",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "churn_level",
                      "type": "string",
                      "label": "Churn level",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "state",
                      "type": "string",
                      "label": "Billing state",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "country",
                      "type": "string",
                      "label": "Country",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "locale",
                      "type": "string",
                      "label": "Language",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "tenure_days",
                      "type": "number",
                      "label": "Days as a customer",
                      "ops": [
                        "eq",
                        "neq",
                        "gt",
                        "gte",
                        "lt",
                        "lte",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "flagged",
                      "type": "boolean",
                      "label": "Early signal (FLAGGED)",
                      "ops": [
                        "eq",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "mrr_cents",
                      "type": "number",
                      "label": "MRR (minor unit)",
                      "ops": [
                        "eq",
                        "neq",
                        "gt",
                        "gte",
                        "lt",
                        "lte",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "nochurn",
                      "field": "support_contacts",
                      "type": "string",
                      "label": "Support conversations, last 30 days (0, 1–2, 3+)",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "stripe",
                      "field": "plan",
                      "type": "string",
                      "label": "Plan",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "stripe",
                      "field": "interval",
                      "type": "string",
                      "label": "Billing interval",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "stripe",
                      "field": "currency",
                      "type": "string",
                      "label": "Currency",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "stripe",
                      "field": "payment_method",
                      "type": "string",
                      "label": "Payment method category",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "stripe",
                      "field": "card_country",
                      "type": "string",
                      "label": "Card country",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    },
                    {
                      "source": "stripe",
                      "field": "card_funding",
                      "type": "string",
                      "label": "Card funding",
                      "ops": [
                        "eq",
                        "neq",
                        "in",
                        "not_in",
                        "contains",
                        "exists",
                        "not_exists"
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/SegmentFieldList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/segments": {
      "parameters": [
        {
          "name": "flow",
          "in": "query",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "flw_… or a kind."
        }
      ],
      "get": {
        "summary": "Segments of a flow",
        "tags": [
          "Flows"
        ],
        "operationId": "listSegments",
        "description": "In match order: the first segment whose conditions match wins; Everyone else is always last.",
        "responses": {
          "200": {
            "description": "with Everyone else",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "has_more": false,
                  "next_cursor": null,
                  "data": [
                    {
                      "object": "segment",
                      "id": "seg_H45K9royPTDptPfa",
                      "flow": "flw_adUax5LQBJ5bnTPF",
                      "name": "Everyone else",
                      "position": 1000000,
                      "is_default": true,
                      "conditions": {
                        "any": []
                      },
                      "overrides": {},
                      "customers_count": null,
                      "mrr_cents": null,
                      "counted_at": null,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/SegmentList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Create a segment",
        "tags": [
          "Flows"
        ],
        "operationId": "createSegment",
        "parameters": [],
        "responses": {
          "201": {
            "description": "created before Everyone else",
            "content": {
              "application/json": {
                "example": {
                  "object": "segment",
                  "id": "seg_oAiWGSuQoPvjdK6X",
                  "flow": "flw_CE1Qp1CyPvsjbJXE",
                  "name": "High risk",
                  "position": 0,
                  "is_default": false,
                  "conditions": {
                    "any": [
                      {
                        "all": [
                          {
                            "source": "nochurn",
                            "field": "churn_score",
                            "op": "gte",
                            "value": 70
                          }
                        ]
                      }
                    ]
                  },
                  "overrides": {
                    "cancel_mode": "immediately"
                  },
                  "customers_count": null,
                  "mrr_cents": null,
                  "counted_at": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Segment"
                }
              }
            }
          },
          "422": {
            "description": "unknown field",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "conditions": {
                    "type": "object",
                    "description": "{ any: [ { all: [ { source, field, op, value } ] } ] }"
                  },
                  "overrides": {
                    "type": "object",
                    "description": "THEN: patch of the flow config for this segment. A null in send_window means follow the flow (not the account): it is not stored."
                  }
                }
              },
              "example": {
                "name": "High risk",
                "conditions": {
                  "any": [
                    {
                      "all": [
                        {
                          "source": "nochurn",
                          "field": "churn_score",
                          "op": "gte",
                          "value": 70
                        }
                      ]
                    }
                  ]
                },
                "overrides": {
                  "cancel_mode": "immediately"
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/segments/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Edit a segment",
        "tags": [
          "Flows"
        ],
        "operationId": "updateSegment",
        "description": "Overrides follow the flow draft's rules: known sections only; the cancel flow's texts and CSS are normalized. A null in send_window means follow the flow (not the account): it is not stored.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "a null in send_window follows the flow: not stored",
            "content": {
              "application/json": {
                "example": {
                  "object": "segment",
                  "id": "seg_xZtZrJpMirXY6Z62",
                  "flow": "flw_yJRnuy8MJBfpxCpi",
                  "name": "VIP",
                  "position": 0,
                  "is_default": false,
                  "conditions": {
                    "any": [
                      {
                        "all": [
                          {
                            "op": "gte",
                            "field": "churn_score",
                            "value": 70,
                            "source": "nochurn"
                          }
                        ]
                      }
                    ]
                  },
                  "overrides": {
                    "cancel_mode": "immediately"
                  },
                  "customers_count": null,
                  "mrr_cents": null,
                  "counted_at": null,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Segment"
                }
              }
            }
          },
          "400": {
            "description": "unknown override section",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "conditions": {
                    "type": "object"
                  },
                  "overrides": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "overrides": {
                  "cancel_mode": "immediately"
                }
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Delete a segment",
        "tags": [
          "Flows"
        ],
        "operationId": "deleteSegment",
        "description": "Its draft A/B tests go with it. A segment whose tests ran keeps their results: it cannot be deleted (409).",
        "responses": {
          "200": {
            "description": "deleted",
            "content": {
              "application/json": {
                "example": {
                  "object": "segment",
                  "id": "seg_MRdMzGuyEFdBKekA",
                  "flow": "flw_1BGQe19r5oviKRhH",
                  "name": "VIP",
                  "position": 0,
                  "is_default": false,
                  "conditions": {
                    "any": [
                      {
                        "all": [
                          {
                            "op": "gte",
                            "field": "churn_score",
                            "value": 70,
                            "source": "nochurn"
                          }
                        ]
                      }
                    ]
                  },
                  "overrides": {},
                  "customers_count": null,
                  "mrr_cents": null,
                  "counted_at": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "deleted": true
                },
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "deleted"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "it has A/B test results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/segments/preview": {
      "post": {
        "summary": "Count the customers of conditions",
        "tags": [
          "Flows"
        ],
        "operationId": "previewSegment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "live count and MRR",
            "content": {
              "application/json": {
                "example": {
                  "object": "segment_preview",
                  "customers_count": 1,
                  "mrr_cents": 4900,
                  "counted_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SegmentPreview"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "conditions"
                ],
                "properties": {
                  "conditions": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "conditions": {
                  "any": [
                    {
                      "all": [
                        {
                          "source": "nochurn",
                          "field": "churn_score",
                          "op": "gte",
                          "value": 70
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/experiments": {
      "post": {
        "summary": "Draft an A/B test",
        "tags": [
          "Flows"
        ],
        "operationId": "createExperiment",
        "description": "2 to 4 variants (A is the control). The winner is picked automatically when a variant is the best with probability ≥ threshold and every variant has min_sample runs.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "a draft",
            "content": {
              "application/json": {
                "example": {
                  "object": "experiment",
                  "id": "abt_kpD34ZFDVT9aMwBJ",
                  "flow": "flw_uBtoswcyqSYy5vqi",
                  "segment": null,
                  "name": "Discount vs pause",
                  "target": "offer",
                  "goal_metric": "saved",
                  "status": "draft",
                  "auto_pick": true,
                  "threshold": 0.95,
                  "min_sample": 100,
                  "started_at": null,
                  "stopped_at": null,
                  "winner": null,
                  "stats_at": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "variants": [
                    {
                      "id": "abv_EwrAK2JyfLNW7vMd",
                      "label": "A",
                      "is_control": true,
                      "weight": 50,
                      "overrides": {},
                      "exposures": 0,
                      "conversions": 0,
                      "conversion_rate": null,
                      "probability_best": null
                    },
                    {
                      "id": "abv_uBa3iRjgQ3iMXEQj",
                      "label": "B",
                      "is_control": false,
                      "weight": 50,
                      "overrides": {
                        "cancel_mode": "immediately"
                      },
                      "exposures": 0,
                      "conversions": 0,
                      "conversion_rate": null,
                      "probability_best": null
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/Experiment"
                }
              }
            }
          },
          "400": {
            "description": "invalid variants (duplicate labels, no single control), unknown override section, or another flow's segment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "flow",
                  "name",
                  "target",
                  "goal_metric",
                  "variants"
                ],
                "properties": {
                  "flow": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "target": {
                    "type": "string",
                    "enum": [
                      "offer",
                      "text",
                      "timing",
                      "design",
                      "channel"
                    ]
                  },
                  "goal_metric": {
                    "type": "string",
                    "enum": [
                      "saved",
                      "card_updated_before_renewal",
                      "recovered_within_7d",
                      "won_back"
                    ]
                  },
                  "segment": {
                    "type": "string"
                  },
                  "auto_pick": {
                    "type": "boolean"
                  },
                  "threshold": {
                    "type": "number"
                  },
                  "min_sample": {
                    "type": "integer"
                  },
                  "variants": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string"
                        },
                        "weight": {
                          "type": "integer"
                        },
                        "overrides": {
                          "type": "object",
                          "description": "A null in send_window means follow the flow (not the account): it is not stored."
                        },
                        "is_control": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "flow": "cancel",
                "name": "Discount vs pause",
                "target": "offer",
                "goal_metric": "saved",
                "variants": [
                  {
                    "weight": 50
                  },
                  {
                    "weight": 50,
                    "overrides": {
                      "cancel_mode": "immediately"
                    }
                  }
                ]
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "A/B tests",
        "tags": [
          "Flows"
        ],
        "operationId": "listExperiments",
        "parameters": [
          {
            "name": "flow",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "newest first",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ExperimentList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/experiments/{id}/{action}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        },
        {
          "name": "action",
          "in": "path",
          "schema": {
            "type": "string",
            "enum": [
              "start",
              "stop",
              "pick"
            ]
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Start, stop, or pick a winner",
        "tags": [
          "Flows"
        ],
        "operationId": "transitionExperiment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "started",
            "content": {
              "application/json": {
                "example": {
                  "object": "experiment",
                  "id": "abt_11NKySG6YQfrocfH",
                  "flow": "flw_PnbiXGGd1R1skZwB",
                  "segment": null,
                  "name": "T",
                  "target": "offer",
                  "goal_metric": "saved",
                  "status": "running",
                  "auto_pick": true,
                  "threshold": 0.95,
                  "min_sample": 100,
                  "started_at": "2026-11-04T10:00:00Z",
                  "stopped_at": null,
                  "winner": null,
                  "stats_at": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "variants": [
                    {
                      "id": "abv_8bRciRLXrwnVhqgf",
                      "label": "A",
                      "is_control": false,
                      "weight": 50,
                      "overrides": {},
                      "exposures": 0,
                      "conversions": 0,
                      "conversion_rate": null,
                      "probability_best": null
                    },
                    {
                      "id": "abv_qgyEn9yU48DBcVcW",
                      "label": "B",
                      "is_control": false,
                      "weight": 50,
                      "overrides": {},
                      "exposures": 0,
                      "conversions": 0,
                      "conversion_rate": null,
                      "probability_best": null
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/Experiment"
                }
              }
            }
          },
          "409": {
            "description": "only a running or stopped test gets a winner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "variant": {
                    "type": "string",
                    "description": "pick: abv_… or label."
                  }
                }
              },
              "example": {}
            }
          }
        }
      }
    },
    "/v1/account/send_schedule/flow_overrides": {
      "get": {
        "summary": "Flows that set their own send window options",
        "tags": [
          "Account"
        ],
        "operationId": "listSendScheduleFlowOverrides",
        "description": "In the current mode: the options the published version of each flow sets itself (instead of following Settings › Send schedule), those its draft sets, and the segments (THEN) and A/B variants of the flow that set them (no mode: listed in both; the variants of a running test with applies false). Only flows with at least one.",
        "parameters": [
          {
            "name": "keys[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "audience",
                  "respect_customer_timezone",
                  "skip_weekends",
                  "skip_public_holidays",
                  "windows",
                  "quiet_hours",
                  "days_off"
                ]
              }
            },
            "description": "Only these options (all by default)."
          }
        ],
        "responses": {
          "200": {
            "description": "a flow that follows the account, but one of its segments and A/B variants do not",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "send_schedule_flow_override",
                      "flow": {
                        "id": "flw_3yDzGfxYA3qXrkwX",
                        "kind": "recovery",
                        "name": "Recovery flow"
                      },
                      "version": {
                        "id": "flv_dS81MgAgS7sx1js4",
                        "number": 1,
                        "published_to": "live"
                      },
                      "published": {
                        "quiet_hours": {
                          "to": "08:00",
                          "from": "21:00",
                          "enabled": true
                        }
                      },
                      "draft": {
                        "quiet_hours": {
                          "to": "08:00",
                          "from": "21:00",
                          "enabled": true
                        }
                      },
                      "segments": [],
                      "variants": []
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SendScheduleFlowOverrideList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/account/send_schedule/apply_to_flows": {
      "post": {
        "summary": "Make flows follow Settings › Send schedule",
        "tags": [
          "Account"
        ],
        "operationId": "applySendScheduleToFlows",
        "description": "Sets the options to null (follow the account) in the flows that set them: the published version of the current mode is copied into a new version with only those options changed (other draft changes are never published), and the draft loses them too. The segments (THEN) and A/B variants of those flows lose them in place (removed, so they follow their flow; they have no mode and no versions, so this changes both modes). The variants of a running test are not changed. Owners and admins. Nothing to change: nothing happens.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "a segment and a draft test's variant follow their flow again; a running test is left alone",
            "content": {
              "application/json": {
                "example": {
                  "object": "send_schedule_flows_applied",
                  "livemode": true,
                  "applied": 1,
                  "counts": {
                    "flows": 1,
                    "segments": 0,
                    "variants": 0
                  },
                  "data": [
                    {
                      "object": "send_schedule_flow_applied",
                      "flow": {
                        "id": "flw_cRisxrrykqgKPHdX",
                        "kind": "recovery",
                        "name": "Recovery flow"
                      },
                      "version": {
                        "id": "flv_taVfx8DNTK3tKcwE",
                        "number": 2,
                        "published_to": "live"
                      },
                      "published": {
                        "quiet_hours": {
                          "to": "08:00",
                          "from": "21:00",
                          "enabled": true
                        },
                        "respect_customer_timezone": true
                      },
                      "draft": {
                        "quiet_hours": {
                          "to": "08:00",
                          "from": "21:00",
                          "enabled": true
                        },
                        "respect_customer_timezone": true
                      },
                      "segments": [],
                      "variants": []
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/SendScheduleFlowsApplied"
                }
              }
            }
          },
          "400": {
            "description": "unknown option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "a flow of another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "keys": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "audience",
                        "respect_customer_timezone",
                        "skip_weekends",
                        "skip_public_holidays",
                        "windows",
                        "quiet_hours",
                        "days_off"
                      ]
                    },
                    "description": "All options by default."
                  },
                  "flow_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "All flows that override by default. A flow's segments and A/B variants are applied with it."
                  }
                }
              },
              "example": {
                "keys": [
                  "quiet_hours",
                  "respect_customer_timezone"
                ],
                "flow_ids": [
                  "flw_cRisxrrykqgKPHdX"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/account/send_schedule": {
      "get": {
        "summary": "When NoChurn may contact your customers",
        "tags": [
          "Account"
        ],
        "operationId": "getSendSchedule",
        "responses": {
          "200": {
            "description": "B2B preset by default",
            "content": {
              "application/json": {
                "example": {
                  "object": "send_schedule",
                  "audience": null,
                  "overrides": {},
                  "effective": {
                    "audience": "b2b",
                    "respect_customer_timezone": true,
                    "quiet_hours": {
                      "enabled": true,
                      "from": "21:00",
                      "to": "08:00"
                    },
                    "days_off": [],
                    "skip_weekends": true,
                    "skip_public_holidays": true,
                    "windows": {
                      "weekday": [
                        [
                          "09:00",
                          "17:00"
                        ]
                      ],
                      "weekend": []
                    }
                  },
                  "presets": {
                    "b2b": {
                      "respect_customer_timezone": true,
                      "quiet_hours": {
                        "enabled": true,
                        "from": "21:00",
                        "to": "08:00"
                      },
                      "days_off": [],
                      "skip_weekends": true,
                      "skip_public_holidays": true,
                      "windows": {
                        "weekday": [
                          [
                            "09:00",
                            "17:00"
                          ]
                        ],
                        "weekend": []
                      }
                    },
                    "b2c": {
                      "respect_customer_timezone": true,
                      "quiet_hours": {
                        "enabled": true,
                        "from": "21:00",
                        "to": "08:00"
                      },
                      "days_off": [],
                      "skip_weekends": false,
                      "skip_public_holidays": false,
                      "windows": {
                        "weekday": [
                          [
                            "11:00",
                            "13:00"
                          ],
                          [
                            "18:00",
                            "21:00"
                          ]
                        ],
                        "weekend": [
                          [
                            "10:00",
                            "20:00"
                          ]
                        ]
                      }
                    }
                  },
                  "suggested_audience": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SendSchedule"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Change the audience or any option",
        "tags": [
          "Account"
        ],
        "operationId": "updateSendSchedule",
        "description": "Each option can be switched on or off; null removes an override (back to the preset).",
        "parameters": [],
        "responses": {
          "200": {
            "description": "B2C, with Christmas Eve off",
            "content": {
              "application/json": {
                "example": {
                  "object": "send_schedule",
                  "audience": "b2c",
                  "overrides": {
                    "days_off": [
                      "2026-12-24"
                    ]
                  },
                  "effective": {
                    "audience": "b2c",
                    "respect_customer_timezone": true,
                    "quiet_hours": {
                      "enabled": true,
                      "from": "21:00",
                      "to": "08:00"
                    },
                    "days_off": [
                      "2026-12-24"
                    ],
                    "skip_weekends": false,
                    "skip_public_holidays": false,
                    "windows": {
                      "weekday": [
                        [
                          "11:00",
                          "13:00"
                        ],
                        [
                          "18:00",
                          "21:00"
                        ]
                      ],
                      "weekend": [
                        [
                          "10:00",
                          "20:00"
                        ]
                      ]
                    }
                  },
                  "presets": {
                    "b2b": {
                      "respect_customer_timezone": true,
                      "quiet_hours": {
                        "enabled": true,
                        "from": "21:00",
                        "to": "08:00"
                      },
                      "days_off": [],
                      "skip_weekends": true,
                      "skip_public_holidays": true,
                      "windows": {
                        "weekday": [
                          [
                            "09:00",
                            "17:00"
                          ]
                        ],
                        "weekend": []
                      }
                    },
                    "b2c": {
                      "respect_customer_timezone": true,
                      "quiet_hours": {
                        "enabled": true,
                        "from": "21:00",
                        "to": "08:00"
                      },
                      "days_off": [],
                      "skip_weekends": false,
                      "skip_public_holidays": false,
                      "windows": {
                        "weekday": [
                          [
                            "11:00",
                            "13:00"
                          ],
                          [
                            "18:00",
                            "21:00"
                          ]
                        ],
                        "weekend": [
                          [
                            "10:00",
                            "20:00"
                          ]
                        ]
                      }
                    }
                  },
                  "suggested_audience": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SendSchedule"
                }
              }
            }
          },
          "400": {
            "description": "unknown option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "audience": {
                    "type": "string",
                    "enum": [
                      "b2b",
                      "b2c"
                    ]
                  },
                  "skip_weekends": {
                    "type": "boolean",
                    "nullable": true
                  },
                  "skip_public_holidays": {
                    "type": "boolean",
                    "nullable": true
                  },
                  "respect_customer_timezone": {
                    "type": "boolean",
                    "nullable": true
                  },
                  "windows": {
                    "type": "object",
                    "nullable": true
                  },
                  "quiet_hours": {
                    "type": "object",
                    "nullable": true
                  },
                  "days_off": {
                    "type": "array",
                    "nullable": true,
                    "items": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              },
              "example": {
                "audience": "b2c",
                "days_off": [
                  "2026-12-24"
                ]
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/customers/{id}/next_send_slot": {
      "get": {
        "summary": "When NoChurn would contact this customer now",
        "tags": [
          "Customers"
        ],
        "operationId": "getNextSendSlot",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "flow",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "prevention",
                "recovery",
                "cancel",
                "winback"
              ]
            },
            "description": "Apply this flow's own send window."
          }
        ],
        "responses": {
          "200": {
            "description": "the next slot in the customer's time zone, with the reasons of a delay",
            "content": {
              "application/json": {
                "example": {
                  "object": "send_slot",
                  "customer": "cst_HzkSj31QvesBjiLa",
                  "at": "2026-11-04T11:00:00+01:00",
                  "timezone": "Europe/Berlin",
                  "timezone_source": "address",
                  "delayed": false,
                  "reasons": [],
                  "audience": "b2b"
                },
                "schema": {
                  "$ref": "#/components/schemas/SendSlot"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/sending_domains": {
      "get": {
        "summary": "List sending domains",
        "tags": [
          "Email domain"
        ],
        "operationId": "listSendingDomains",
        "responses": {
          "200": {
            "description": "the account's domain",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "sending_domain",
                      "id": "sdm_ua6hYT8P6PmrVtzo",
                      "domain": "acme.com",
                      "sender_name": "Acme billing",
                      "sender_email": "billing@acme.com",
                      "sending_host": "nc-utj7.acme.com",
                      "status": "pending",
                      "provider": "fake",
                      "provisioning_error": null,
                      "verified_at": null,
                      "last_checked_at": null,
                      "existing_dmarc": false,
                      "fallback_address": "acme@mail.nochurn.sh",
                      "from_address": "acme@mail.nochurn.sh",
                      "dns_records": [
                        {
                          "type": "TXT",
                          "host": "nc-utj7.acme.com",
                          "value": "v=spf1 include:mailgun.org ~all",
                          "purpose": "sending (SPF)",
                          "optional": false,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        },
                        {
                          "type": "CNAME",
                          "host": "pdk1._domainkey.nc-utj7.acme.com",
                          "value": "pdk1._domainkey.0ae1cc2a3899306ae7550445.fake-dkim.mailgun.invalid",
                          "purpose": "signature (DKIM)",
                          "optional": false,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        },
                        {
                          "type": "CNAME",
                          "host": "pdk2._domainkey.nc-utj7.acme.com",
                          "value": "pdk2._domainkey.0ae1cc2a3899306ae7550445.fake-dkim.mailgun.invalid",
                          "purpose": "signature (DKIM)",
                          "optional": false,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        },
                        {
                          "type": "CNAME",
                          "host": "email.nc-utj7.acme.com",
                          "value": "eu.mailgun.org",
                          "purpose": "link tracking",
                          "optional": false,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        },
                        {
                          "type": "MX",
                          "host": "nc-utj7.acme.com",
                          "value": "10 mxa.eu.mailgun.org",
                          "purpose": "replies, bounces",
                          "optional": false,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        },
                        {
                          "type": "MX",
                          "host": "nc-utj7.acme.com",
                          "value": "10 mxb.eu.mailgun.org",
                          "purpose": "replies, bounces",
                          "optional": false,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        },
                        {
                          "type": "TXT",
                          "host": "_dmarc.acme.com",
                          "value": "v=DMARC1; p=none; rua=mailto:dmarc@acme.com",
                          "purpose": "policy",
                          "optional": true,
                          "status": "pending",
                          "found": null,
                          "checked_at": null
                        }
                      ],
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/SendingDomainList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Add your sending domain",
        "tags": [
          "Email domain"
        ],
        "operationId": "createSendingDomain",
        "description": "Emails go out from a random nc-xxxx subdomain, created at Mailgun: your main domain and your team's mailboxes are never touched. The domain starts in `provisioning`: its DNS records (SPF, DKIM, link tracking, MX) arrive within seconds, once Mailgun answered. Until the records are found and Mailgun confirms them, emails come from <account>@mail.nochurn.sh.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "domain added, records to create",
            "content": {
              "application/json": {
                "example": {
                  "object": "sending_domain",
                  "id": "sdm_R37t9AFsE55k3v9L",
                  "domain": "acme.com",
                  "sender_name": "Acme billing",
                  "sender_email": "billing@acme.com",
                  "sending_host": "nc-769z.acme.com",
                  "status": "provisioning",
                  "provider": "fake",
                  "provisioning_error": null,
                  "verified_at": null,
                  "last_checked_at": null,
                  "existing_dmarc": false,
                  "fallback_address": "acme@mail.nochurn.sh",
                  "from_address": "acme@mail.nochurn.sh",
                  "dns_records": [],
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          },
          "422": {
            "description": "sender outside the domain",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "sender_email"
                ],
                "properties": {
                  "domain": {
                    "type": "string",
                    "example": "acme.com"
                  },
                  "sender_email": {
                    "type": "string",
                    "example": "billing@acme.com"
                  },
                  "sender_name": {
                    "type": "string",
                    "example": "Acme billing"
                  }
                }
              },
              "example": {
                "domain": "acme.com",
                "sender_email": "billing@acme.com",
                "sender_name": "Acme billing"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/sending_domains/{id}/verify": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Check the DNS records",
        "tags": [
          "Email domain"
        ],
        "operationId": "verifySendingDomain",
        "responses": {
          "200": {
            "description": "every record found",
            "content": {
              "application/json": {
                "example": {
                  "object": "sending_domain",
                  "id": "sdm_XajGHMam4v9qgRkg",
                  "domain": "acme.com",
                  "sender_name": "Acme billing",
                  "sender_email": "billing@acme.com",
                  "sending_host": "nc-4hmm.acme.com",
                  "status": "verified",
                  "provider": "fake",
                  "provisioning_error": null,
                  "verified_at": "2026-11-04T10:00:00Z",
                  "last_checked_at": "2026-11-04T10:00:00Z",
                  "existing_dmarc": false,
                  "fallback_address": "acme@mail.nochurn.sh",
                  "from_address": "billing@acme.com",
                  "dns_records": [
                    {
                      "type": "TXT",
                      "host": "nc-4hmm.acme.com",
                      "value": "v=spf1 include:mailgun.org ~all",
                      "purpose": "sending (SPF)",
                      "optional": false,
                      "status": "verified",
                      "found": "v=spf1 include:mailgun.org ~all",
                      "checked_at": "2026-11-04T10:00:00Z"
                    },
                    {
                      "type": "CNAME",
                      "host": "pdk1._domainkey.nc-4hmm.acme.com",
                      "value": "pdk1._domainkey.6b66132b30f8f0fe6a014a82.fake-dkim.mailgun.invalid",
                      "purpose": "signature (DKIM)",
                      "optional": false,
                      "status": "verified",
                      "found": "pdk1._domainkey.6b66132b30f8f0fe6a014a82.fake-dkim.mailgun.invalid",
                      "checked_at": "2026-11-04T10:00:00Z"
                    },
                    {
                      "type": "CNAME",
                      "host": "pdk2._domainkey.nc-4hmm.acme.com",
                      "value": "pdk2._domainkey.6b66132b30f8f0fe6a014a82.fake-dkim.mailgun.invalid",
                      "purpose": "signature (DKIM)",
                      "optional": false,
                      "status": "verified",
                      "found": "pdk2._domainkey.6b66132b30f8f0fe6a014a82.fake-dkim.mailgun.invalid",
                      "checked_at": "2026-11-04T10:00:00Z"
                    },
                    {
                      "type": "CNAME",
                      "host": "email.nc-4hmm.acme.com",
                      "value": "eu.mailgun.org",
                      "purpose": "link tracking",
                      "optional": false,
                      "status": "verified",
                      "found": "eu.mailgun.org",
                      "checked_at": "2026-11-04T10:00:00Z"
                    },
                    {
                      "type": "MX",
                      "host": "nc-4hmm.acme.com",
                      "value": "10 mxa.eu.mailgun.org",
                      "purpose": "replies, bounces",
                      "optional": false,
                      "status": "verified",
                      "found": "10 mxa.eu.mailgun.org",
                      "checked_at": "2026-11-04T10:00:00Z"
                    },
                    {
                      "type": "MX",
                      "host": "nc-4hmm.acme.com",
                      "value": "10 mxb.eu.mailgun.org",
                      "purpose": "replies, bounces",
                      "optional": false,
                      "status": "verified",
                      "found": "10 mxa.eu.mailgun.org",
                      "checked_at": "2026-11-04T10:00:00Z"
                    },
                    {
                      "type": "TXT",
                      "host": "_dmarc.acme.com",
                      "value": "v=DMARC1; p=none; rua=mailto:dmarc@acme.com",
                      "purpose": "policy",
                      "optional": true,
                      "status": "verified",
                      "found": "v=DMARC1; p=none; rua=mailto:dmarc@acme.com",
                      "checked_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          },
          "409": {
            "description": "records still being prepared by Mailgun",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/sending_domains/{id}/regenerate": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "New random subdomain",
        "tags": [
          "Email domain"
        ],
        "operationId": "regenerateSendingDomain",
        "description": "Only while the domain is not verified: the old nc-xxxx is deleted at Mailgun and the new one prepared (status provisioning, new DNS records).",
        "responses": {
          "200": {
            "description": "a new subdomain, being prepared",
            "content": {
              "application/json": {
                "example": {
                  "object": "sending_domain",
                  "id": "sdm_M8nVdo71jUpjJkUt",
                  "domain": "acme.com",
                  "sender_name": "Acme billing",
                  "sender_email": "billing@acme.com",
                  "sending_host": "nc-aya7.acme.com",
                  "status": "provisioning",
                  "provider": "fake",
                  "provisioning_error": null,
                  "verified_at": null,
                  "last_checked_at": null,
                  "existing_dmarc": false,
                  "fallback_address": "acme@mail.nochurn.sh",
                  "from_address": "acme@mail.nochurn.sh",
                  "dns_records": [],
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          },
          "409": {
            "description": "already verified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/sending_domains/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Retrieve the sending domain",
        "tags": [
          "Email domain"
        ],
        "operationId": "getSendingDomain",
        "responses": {
          "200": {
            "description": "a domain Mailgun could not prepare, with what to do",
            "content": {
              "application/json": {
                "example": {
                  "object": "sending_domain",
                  "id": "sdm_UWVmvucdELPGdftg",
                  "domain": "acme.com",
                  "sender_name": "Acme billing",
                  "sender_email": "billing@acme.com",
                  "sending_host": "nc-jtpt.acme.com",
                  "status": "provisioning",
                  "provider": "fake",
                  "provisioning_error": {
                    "code": "email_domain_provisioning_failed",
                    "message": "NoChurn could not prepare the DNS records of this domain with its email provider.",
                    "fix": "Click Regenerate to try with a new subdomain. Until then, emails keep coming from your NoChurn address.",
                    "docs_url": "https://nochurn.sh/docs/errors#email_domain_provisioning_failed"
                  },
                  "verified_at": null,
                  "last_checked_at": null,
                  "existing_dmarc": false,
                  "fallback_address": "acme@mail.nochurn.sh",
                  "from_address": "acme@mail.nochurn.sh",
                  "dns_records": [],
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "patch": {
        "summary": "Change the sender",
        "tags": [
          "Email domain"
        ],
        "operationId": "updateSendingDomain",
        "parameters": [],
        "responses": {
          "200": {
            "description": "sender changed",
            "content": {
              "application/json": {
                "example": {
                  "object": "sending_domain",
                  "id": "sdm_q2CtBG9Bvps85iEa",
                  "domain": "acme.com",
                  "sender_name": "Acme billing",
                  "sender_email": "team@acme.com",
                  "sending_host": "nc-qa62.acme.com",
                  "status": "pending",
                  "provider": "fake",
                  "provisioning_error": null,
                  "verified_at": null,
                  "last_checked_at": null,
                  "existing_dmarc": false,
                  "fallback_address": "acme@mail.nochurn.sh",
                  "from_address": "acme@mail.nochurn.sh",
                  "dns_records": [
                    {
                      "type": "TXT",
                      "host": "nc-qa62.acme.com",
                      "value": "v=spf1 include:mailgun.org ~all",
                      "purpose": "sending (SPF)",
                      "optional": false,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    },
                    {
                      "type": "CNAME",
                      "host": "pdk1._domainkey.nc-qa62.acme.com",
                      "value": "pdk1._domainkey.a1b412bf3158ea43cca9af10.fake-dkim.mailgun.invalid",
                      "purpose": "signature (DKIM)",
                      "optional": false,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    },
                    {
                      "type": "CNAME",
                      "host": "pdk2._domainkey.nc-qa62.acme.com",
                      "value": "pdk2._domainkey.a1b412bf3158ea43cca9af10.fake-dkim.mailgun.invalid",
                      "purpose": "signature (DKIM)",
                      "optional": false,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    },
                    {
                      "type": "CNAME",
                      "host": "email.nc-qa62.acme.com",
                      "value": "eu.mailgun.org",
                      "purpose": "link tracking",
                      "optional": false,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    },
                    {
                      "type": "MX",
                      "host": "nc-qa62.acme.com",
                      "value": "10 mxa.eu.mailgun.org",
                      "purpose": "replies, bounces",
                      "optional": false,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    },
                    {
                      "type": "MX",
                      "host": "nc-qa62.acme.com",
                      "value": "10 mxb.eu.mailgun.org",
                      "purpose": "replies, bounces",
                      "optional": false,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    },
                    {
                      "type": "TXT",
                      "host": "_dmarc.acme.com",
                      "value": "v=DMARC1; p=none; rua=mailto:dmarc@acme.com",
                      "purpose": "policy",
                      "optional": true,
                      "status": "pending",
                      "found": null,
                      "checked_at": null
                    }
                  ],
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sender_email": {
                    "type": "string"
                  },
                  "sender_name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "sender_email": "team@acme.com"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Remove the domain",
        "tags": [
          "Email domain"
        ],
        "operationId": "deleteSendingDomain",
        "responses": {
          "204": {
            "description": "removed"
          },
          "404": {
            "description": "another account's domain"
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/stripe_connections": {
      "get": {
        "summary": "List connected Stripe accounts (current mode)",
        "tags": [
          "Stripe"
        ],
        "operationId": "listStripeConnections",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "connections, tokens never included",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "stripe_connection",
                      "id": "scn_3oj4t6Cs5RBTByvp",
                      "stripe_account_id": "acct_A",
                      "livemode": false,
                      "status": "active",
                      "smart_retries_mode": "stripe",
                      "internal_id": {
                        "field": null,
                        "defaults": [
                          "metadata.user_id"
                        ],
                        "fields": [
                          "metadata.<name>"
                        ]
                      },
                      "backfill": {
                        "status": "done",
                        "progress": {
                          "customers": 3
                        },
                        "error": null,
                        "started_at": null,
                        "finished_at": null
                      },
                      "history": {
                        "status": "pending",
                        "since": null,
                        "progress": {},
                        "error": null,
                        "started_at": null,
                        "finished_at": null
                      },
                      "last_webhook_at": "2026-11-04T09:58:00.000Z",
                      "last_webhook_type": "invoice.payment_failed",
                      "disconnected_at": null,
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/StripeConnectionList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/stripe_connections/authorize": {
      "post": {
        "summary": "Get the Stripe Connect URL to open in the browser",
        "tags": [
          "Stripe"
        ],
        "operationId": "authorizeStripe",
        "responses": {
          "200": {
            "description": "Stripe Connect OAuth URL, with a signed state",
            "content": {
              "application/json": {
                "example": {
                  "object": "stripe_authorize_url",
                  "url": "https://connect.stripe.com/oauth/authorize?client_id=ca_test_dummy&response_type=code&scope=read_write&state=eyJfcmFpbHMiOnsiZGF0YSI6eyJhY2NvdW50X2lkIjoiYWNjX1JpVWpWOUFqNTNYeTh3djQiLCJ1c2VyX2lkIjpudWxsLCJsaXZlbW9kZSI6ZmFsc2UsIm5vbmNlIjoiOGQzZDI1YzA4NDEzNThiNyJ9LCJleHAiOiIyMDI2LTExLTA0VDEwOjMwOjAwLjAwMFoiLCJwdXIiOiJzdHJpcGVfY29ubmVjdCJ9fQ%3D%3D--6ed54c807385687846013962ddefc306141e6dc2",
                  "livemode": false
                },
                "schema": {
                  "$ref": "#/components/schemas/StripeAuthorizeUrl"
                }
              }
            }
          },
          "503": {
            "description": "Stripe not configured on this server",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/stripe_connections/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "patch": {
        "summary": "Choose who handles payment retries, or where your user id is read",
        "tags": [
          "Stripe"
        ],
        "operationId": "updateStripeConnection",
        "parameters": [],
        "responses": {
          "200": {
            "description": "updated",
            "content": {
              "application/json": {
                "example": {
                  "object": "stripe_connection",
                  "id": "scn_4MU3M4xF2KByearr",
                  "stripe_account_id": "acct_A",
                  "livemode": false,
                  "status": "active",
                  "smart_retries_mode": "nochurn",
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id"
                    ],
                    "fields": [
                      "metadata.<name>"
                    ]
                  },
                  "backfill": {
                    "status": "done",
                    "progress": {
                      "customers": 3
                    },
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  },
                  "history": {
                    "status": "pending",
                    "since": null,
                    "progress": {},
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  },
                  "last_webhook_at": null,
                  "last_webhook_type": null,
                  "disconnected_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/StripeConnection"
                }
              }
            }
          },
          "422": {
            "description": "invalid mode",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "smart_retries_mode": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "nochurn"
                    ]
                  },
                  "internal_id_field": {
                    "type": "string",
                    "nullable": true,
                    "example": "metadata.user_id",
                    "description": "Owner / admin. The Stripe customer metadata key holding your app's id (metadata.<key>); null = metadata.<account external_id_metadata_key>."
                  }
                }
              },
              "example": {
                "smart_retries_mode": "nochurn"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Disconnect a Stripe account (synced data is kept)",
        "tags": [
          "Stripe"
        ],
        "operationId": "disconnectStripe",
        "responses": {
          "200": {
            "description": "revoked",
            "content": {
              "application/json": {
                "example": {
                  "object": "stripe_connection",
                  "id": "scn_7U2ckwVunr6xCYko",
                  "stripe_account_id": "acct_A",
                  "livemode": false,
                  "status": "revoked",
                  "smart_retries_mode": "stripe",
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id"
                    ],
                    "fields": [
                      "metadata.<name>"
                    ]
                  },
                  "backfill": {
                    "status": "done",
                    "progress": {
                      "customers": 3
                    },
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  },
                  "history": {
                    "status": "pending",
                    "since": null,
                    "progress": {},
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  },
                  "last_webhook_at": null,
                  "last_webhook_type": null,
                  "disconnected_at": "2026-11-04T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/StripeConnection"
                }
              }
            }
          },
          "404": {
            "description": "connection of another account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/stripe_connections/{id}/backfill": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        }
      ],
      "post": {
        "summary": "Re-run the Stripe import (idempotent)",
        "tags": [
          "Stripe"
        ],
        "operationId": "backfillStripe",
        "responses": {
          "202": {
            "description": "import queued",
            "content": {
              "application/json": {
                "example": {
                  "object": "stripe_connection",
                  "id": "scn_29duu2TEteBrZ5L3",
                  "stripe_account_id": "acct_A",
                  "livemode": false,
                  "status": "active",
                  "smart_retries_mode": "stripe",
                  "internal_id": {
                    "field": null,
                    "defaults": [
                      "metadata.user_id"
                    ],
                    "fields": [
                      "metadata.<name>"
                    ]
                  },
                  "backfill": {
                    "status": "pending",
                    "progress": {
                      "customers": 3
                    },
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  },
                  "history": {
                    "status": "pending",
                    "since": null,
                    "progress": {},
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  },
                  "last_webhook_at": null,
                  "last_webhook_type": null,
                  "disconnected_at": null,
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/StripeConnection"
                }
              }
            }
          },
          "409": {
            "description": "already running",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/account/setup": {
      "get": {
        "summary": "Setup checklist: what is installed and what is missing",
        "tags": [
          "Account"
        ],
        "operationId": "getSetup",
        "responses": {
          "200": {
            "description": "billing by API: no Stripe import step; an active support tool counts",
            "content": {
              "application/json": {
                "example": {
                  "object": "setup",
                  "livemode": false,
                  "billing_source": "stripe",
                  "api_base_url": "https://api.nochurn.sh",
                  "complete": false,
                  "steps": [
                    {
                      "key": "billing_connected",
                      "done": true,
                      "fix": null,
                      "skipped": false
                    },
                    {
                      "key": "billing_synced",
                      "done": true,
                      "fix": null,
                      "skipped": false
                    },
                    {
                      "key": "history_imported",
                      "done": false,
                      "fix": "Wait for the 12-month history import to finish, or re-run the import from Integrations.",
                      "skipped": false
                    },
                    {
                      "key": "webhook_received",
                      "done": false,
                      "fix": "Trigger an event in your billing tool (e.g. update a customer) and check it arrives.",
                      "skipped": false
                    },
                    {
                      "key": "api_key_created",
                      "done": true,
                      "fix": null,
                      "skipped": false
                    },
                    {
                      "key": "first_api_call",
                      "done": true,
                      "fix": null,
                      "skipped": false
                    },
                    {
                      "key": "support_connected",
                      "done": false,
                      "fix": "Connect your support tool (Intercom, Zendesk, Crisp, HubSpot, Help Scout or Gorgias) from Integrations.",
                      "skipped": false
                    },
                    {
                      "key": "cancel_modal_detected",
                      "done": false,
                      "fix": "Install the cancel modal on your cancel button: NoChurn detects its first load.",
                      "skipped": false
                    },
                    {
                      "key": "email_domain_verified",
                      "done": false,
                      "fix": "Add your sending domain and its DNS records, then check them (Settings › Email domain).",
                      "skipped": false
                    },
                    {
                      "key": "cancel_flow_published",
                      "done": false,
                      "fix": "Publish your cancel flow (test mode first) from Cancel flow › Settings.",
                      "skipped": false
                    },
                    {
                      "key": "recovery_retries_configured",
                      "done": true,
                      "fix": null,
                      "skipped": false
                    },
                    {
                      "key": "prevention_signals_enabled",
                      "done": true,
                      "fix": null,
                      "skipped": false
                    }
                  ],
                  "onboarding": {
                    "step": "billing",
                    "skipped": [],
                    "completed_at": null
                  },
                  "counters": {
                    "customers_imported": 0,
                    "active_customers": 0,
                    "mrr": [],
                    "failed_payments_to_recover": {
                      "count": 0,
                      "amounts": []
                    },
                    "high_risk_customers": 0,
                    "scored_customers": 0,
                    "first_api_call_at": "2026-11-04T10:00:00.000Z",
                    "customers_with_multiple_subscriptions": 0,
                    "sessions_without_subscription_30d": 0
                  },
                  "history_import": {
                    "status": "pending",
                    "since": null,
                    "progress": {},
                    "error": null,
                    "started_at": null,
                    "finished_at": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Setup"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/memberships": {
      "get": {
        "summary": "Team members of the account",
        "tags": [
          "Team"
        ],
        "operationId": "listMemberships",
        "responses": {
          "200": {
            "description": "members with their role and 2FA status",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "membership",
                      "id": "mbr_cUfc8mTHAU7Eg2co",
                      "role": "member",
                      "user": {
                        "id": "usr_Vv4SRgjqTic423gn",
                        "email": "user1@example.test",
                        "name": "User",
                        "two_factor_enabled": false,
                        "last_login_at": null
                      },
                      "created_at": "2026-11-04T10:00:00.000Z"
                    },
                    {
                      "object": "membership",
                      "id": "mbr_6TshrrzHbJhPfMdK",
                      "role": "owner",
                      "user": {
                        "id": "usr_zt28zC2o6Zn1qFiY",
                        "email": "owner@acme.test",
                        "name": "User",
                        "two_factor_enabled": false,
                        "last_login_at": "2026-11-04T10:00:00.000Z"
                      },
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MembershipList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/memberships/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        },
        {
          "name": "X-Requested-With",
          "in": "header",
          "schema": {
            "type": "string",
            "enum": [
              "nochurn-dashboard"
            ]
          }
        }
      ],
      "patch": {
        "summary": "Change a member's role",
        "tags": [
          "Team"
        ],
        "operationId": "updateMembership",
        "parameters": [],
        "responses": {
          "200": {
            "description": "role changed",
            "content": {
              "application/json": {
                "example": {
                  "object": "membership",
                  "id": "mbr_3Sff9nvg44dKN6ME",
                  "role": "admin",
                  "user": {
                    "id": "usr_9WtFa8kpPXoZueb9",
                    "email": "user1@example.test",
                    "name": "User",
                    "two_factor_enabled": false,
                    "last_login_at": null
                  },
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          },
          "409": {
            "description": "the last owner cannot step down",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "role"
                ],
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "owner",
                      "admin",
                      "member"
                    ]
                  }
                }
              },
              "example": {
                "role": "admin"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Remove a member (or leave the account)",
        "tags": [
          "Team"
        ],
        "operationId": "removeMembership",
        "responses": {
          "200": {
            "description": "removed",
            "content": {
              "application/json": {
                "example": {
                  "object": "membership",
                  "id": "mbr_P3Pc9sSuX3tF4giw",
                  "role": "admin",
                  "user": {
                    "id": "usr_VHAaeTFR9fDTrsq3",
                    "email": "user1@example.test",
                    "name": "User",
                    "two_factor_enabled": false,
                    "last_login_at": null
                  },
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/invitations": {
      "parameters": [
        {
          "name": "X-Requested-With",
          "in": "header",
          "schema": {
            "type": "string",
            "enum": [
              "nochurn-dashboard"
            ]
          }
        }
      ],
      "post": {
        "summary": "Invite someone (returns the link to send, once)",
        "tags": [
          "Team"
        ],
        "operationId": "createInvitation",
        "parameters": [],
        "responses": {
          "201": {
            "description": "invitation with its link",
            "content": {
              "application/json": {
                "example": {
                  "object": "invitation",
                  "id": "inv_myc1pyd2keYC2jWD",
                  "email": "new@acme.test",
                  "role": "admin",
                  "status": "pending",
                  "url": "http://localhost:5173/invite/Au8qSVmR1hhU9hjFeJsbn4JVkfrKT4Dj",
                  "invited_by": "owner@acme.test",
                  "expires_at": "2026-11-11T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Invitation"
                }
              }
            }
          },
          "409": {
            "description": "already a member",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "owner",
                      "admin",
                      "member"
                    ]
                  }
                }
              },
              "example": {
                "email": "New@Acme.test",
                "role": "admin"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "summary": "Pending invitations",
        "tags": [
          "Team"
        ],
        "operationId": "listInvitations",
        "responses": {
          "200": {
            "description": "pending invitations (links are never shown again)",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "invitation",
                      "id": "inv_2ZfRhgxHtsGee7h6",
                      "email": "a@acme.test",
                      "role": "member",
                      "status": "pending",
                      "url": null,
                      "invited_by": "owner@acme.test",
                      "expires_at": "2026-11-11T10:00:00.000Z",
                      "created_at": "2026-11-04T10:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/InvitationList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/invitations/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "schema": {
            "type": "string"
          },
          "required": true
        },
        {
          "name": "X-Requested-With",
          "in": "header",
          "schema": {
            "type": "string",
            "enum": [
              "nochurn-dashboard"
            ]
          }
        }
      ],
      "delete": {
        "summary": "Cancel a pending invitation",
        "tags": [
          "Team"
        ],
        "operationId": "revokeInvitation",
        "responses": {
          "200": {
            "description": "cancelled",
            "content": {
              "application/json": {
                "example": {
                  "object": "invitation",
                  "id": "inv_EBdyWjdifwMigLWC",
                  "email": "bye@acme.test",
                  "role": "member",
                  "status": "revoked",
                  "url": null,
                  "invited_by": "owner@acme.test",
                  "expires_at": "2026-11-11T10:00:00.000Z",
                  "created_at": "2026-11-04T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Invitation"
                }
              }
            }
          },
          "409": {
            "description": "already used or cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/webhook_endpoints": {
      "get": {
        "summary": "List webhook endpoints",
        "tags": [
          "Developers"
        ],
        "operationId": "listWebhookEndpoints",
        "responses": {
          "200": {
            "description": "the list, never the secrets",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "webhook_endpoint",
                      "id": "whk_qPf7RbQXC4oY7SjA",
                      "livemode": false,
                      "url": "https://hooks.acme.com/x",
                      "description": null,
                      "events": [
                        "*"
                      ],
                      "status": "enabled",
                      "secret": null,
                      "last_status": null,
                      "last_delivery_at": null,
                      "failure_streak": 0,
                      "created_at": "2026-11-04T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Add a webhook endpoint",
        "tags": [
          "Developers"
        ],
        "operationId": "createWebhookEndpoint",
        "description": "Deliveries are signed: NoChurn-Signature: t=<unix>,v1=<HMAC-SHA256 of \"t.body\">. The secret is shown once.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "with its secret, once",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_endpoint",
                  "id": "whk_PjDej35aHSE8rdvD",
                  "livemode": false,
                  "url": "https://hooks.acme.com/nochurn",
                  "description": null,
                  "events": [
                    "recovery.recovered",
                    "billing.retry_requested"
                  ],
                  "status": "enabled",
                  "secret": "whsec_EXAMPLE00000000000000000",
                  "last_status": null,
                  "last_delivery_at": null,
                  "failure_streak": 0,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "422": {
            "description": "not https or an unknown event",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "recovery.opened",
                        "recovery.recovered",
                        "recovery.lost",
                        "outcome.confirmed",
                        "outcome.reverted",
                        "billing.retry_requested",
                        "billing.cancel_requested",
                        "billing.discount_requested",
                        "billing.pause_requested",
                        "billing.downgrade_requested",
                        "customer.scored",
                        "customer.flagged",
                        "customer.access_restricted",
                        "customer.access_restored",
                        "cancel_session.started",
                        "cancel_session.offer_accepted",
                        "cancel_session.completed",
                        "winback.reactivated",
                        "*"
                      ]
                    }
                  },
                  "description": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "url": "https://hooks.acme.com/nochurn",
                "events": [
                  "recovery.recovered",
                  "billing.retry_requested"
                ]
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/webhook_endpoints/{id}/test": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Send a signed test event now",
        "tags": [
          "Developers"
        ],
        "operationId": "testWebhookEndpoint",
        "responses": {
          "201": {
            "description": "delivered",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_delivery",
                  "id": "whd_7Fx1qjkiZwLiEgRG",
                  "livemode": false,
                  "endpoint": "whk_S569GCh7fAHwYDn7",
                  "destination": "http",
                  "event": null,
                  "type": "recovery.opened",
                  "status": "delivered",
                  "attempts": 1,
                  "next_attempt_at": null,
                  "response_status": 200,
                  "response_ms": 42,
                  "error": null,
                  "delivered_at": "2026-11-04T10:00:00Z",
                  "failed_at": null,
                  "replay_of": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "payload": {
                    "id": "nevt_test_5accf2bcd660",
                    "data": {
                      "test": true,
                      "message": "Test webhook from NoChurn"
                    },
                    "type": "test",
                    "object": "event",
                    "created": 1793786400,
                    "livemode": false
                  },
                  "response_excerpt": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/webhook_deliveries": {
      "get": {
        "summary": "Webhooks sent, with their attempts",
        "tags": [
          "Developers"
        ],
        "operationId": "listWebhookDeliveries",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "enum": [
              "pending",
              "retrying",
              "delivered",
              "failed"
            ],
            "schema": {
              "type": "string"
            },
            "description": ":\n * `pending` \n * `retrying` \n * `delivered` \n * `failed` \n "
          },
          {
            "name": "endpoint",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "the list",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/webhook_endpoints/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "summary": "Change an endpoint, disable it or rotate its secret",
        "tags": [
          "Developers"
        ],
        "operationId": "updateWebhookEndpoint",
        "parameters": [],
        "responses": {
          "200": {
            "description": "secret rotated",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_endpoint",
                  "id": "whk_6Z1istZvLLDV7b7p",
                  "livemode": false,
                  "url": "https://hooks.acme.com/x",
                  "description": null,
                  "events": [
                    "*"
                  ],
                  "status": "disabled",
                  "secret": "whsec_EXAMPLE00000000000000000",
                  "last_status": null,
                  "last_delivery_at": null,
                  "failure_streak": 0,
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          }
        },
        "x-audience": "public",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "recovery.opened",
                        "recovery.recovered",
                        "recovery.lost",
                        "outcome.confirmed",
                        "outcome.reverted",
                        "billing.retry_requested",
                        "billing.cancel_requested",
                        "billing.discount_requested",
                        "billing.pause_requested",
                        "billing.downgrade_requested",
                        "customer.scored",
                        "customer.flagged",
                        "customer.access_restricted",
                        "customer.access_restored",
                        "cancel_session.started",
                        "cancel_session.offer_accepted",
                        "cancel_session.completed",
                        "winback.reactivated",
                        "*"
                      ]
                    }
                  },
                  "description": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "enabled",
                      "disabled"
                    ]
                  },
                  "rotate_secret": {
                    "type": "boolean",
                    "description": "The new secret is returned once."
                  }
                }
              },
              "example": {
                "rotate_secret": true,
                "status": "disabled"
              }
            }
          },
          "required": true
        }
      },
      "delete": {
        "summary": "Remove an endpoint",
        "tags": [
          "Developers"
        ],
        "operationId": "deleteWebhookEndpoint",
        "responses": {
          "204": {
            "description": "removed"
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/webhook_deliveries/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "One delivery, with its payload",
        "tags": [
          "Developers"
        ],
        "operationId": "getWebhookDelivery",
        "responses": {
          "200": {
            "description": "found",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_delivery",
                  "id": "whd_FySimzUDNEgANYU3",
                  "livemode": false,
                  "endpoint": "whk_1xbKsAAZvYm1Xmn7",
                  "destination": "http",
                  "event": "nevt_1",
                  "type": "recovery.opened",
                  "status": "failed",
                  "attempts": 7,
                  "next_attempt_at": null,
                  "response_status": null,
                  "response_ms": null,
                  "error": null,
                  "delivered_at": null,
                  "failed_at": null,
                  "replay_of": null,
                  "created_at": "2026-11-04T10:00:00Z",
                  "payload": {
                    "id": "nevt_1",
                    "type": "recovery.opened"
                  },
                  "response_excerpt": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/webhook_deliveries/{id}/replay": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Replay a delivery (same payload, same event id)",
        "tags": [
          "Developers"
        ],
        "operationId": "replayWebhookDelivery",
        "responses": {
          "201": {
            "description": "sent again now",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_delivery",
                  "id": "whd_QgnnVzREeDX8CRSm",
                  "livemode": false,
                  "endpoint": "whk_pbUg1sc39Ttpip9b",
                  "destination": "http",
                  "event": "nevt_1",
                  "type": "recovery.opened",
                  "status": "delivered",
                  "attempts": 1,
                  "next_attempt_at": null,
                  "response_status": 200,
                  "response_ms": 42,
                  "error": null,
                  "delivered_at": "2026-11-04T10:00:00Z",
                  "failed_at": null,
                  "replay_of": "whd_63HJxMXhoVWNsVmW",
                  "created_at": "2026-11-04T10:00:00Z",
                  "payload": {
                    "id": "nevt_1"
                  },
                  "response_excerpt": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryDetail"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    },
    "/v1/cancel_sessions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "X-NoChurn-Customer-Token",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "Origin",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Read a cancel session (widget: reload after an error)",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "getCancelSession",
        "description": "Publishable key + the customer token the session was opened with + allowed Origin, or a secret key. cancel_url_now is only given when the session is created.",
        "responses": {
          "200": {
            "description": "the session at its current step",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_session",
                  "id": "ses_cXpnGr9TadrEogEp",
                  "livemode": false,
                  "step": "reason",
                  "outcome": null,
                  "locale": "en",
                  "holdout": false,
                  "published": true,
                  "customer": {
                    "id": "cst_czsHTg8pzuqqXMB7",
                    "stripe_customer_id": "cus_1",
                    "external_id": "user_42"
                  },
                  "subscription": {
                    "id": "nsub_FDNQxWAvM9FaEmV7",
                    "plan": null,
                    "status": "active",
                    "amount_cents": 4900,
                    "currency": "eur",
                    "interval": "month",
                    "interval_count": 1,
                    "current_period_end": "2026-11-24T10:00:00Z",
                    "cancel_at_period_end": false
                  },
                  "subscriptions": [],
                  "reasons": [
                    {
                      "key": "too_expensive",
                      "label": "It's too expensive",
                      "free_text": false
                    },
                    {
                      "key": "missing_feature",
                      "label": "A feature is missing",
                      "free_text": false
                    },
                    {
                      "key": "not_using",
                      "label": "I don't use it enough",
                      "free_text": false
                    },
                    {
                      "key": "switching",
                      "label": "I'm switching to another tool",
                      "free_text": false
                    },
                    {
                      "key": "temporary",
                      "label": "I only need a break",
                      "free_text": false
                    },
                    {
                      "key": "project_finished",
                      "label": "My project is finished",
                      "free_text": false
                    },
                    {
                      "key": "other",
                      "label": "Something else",
                      "free_text": true
                    }
                  ],
                  "offer": null,
                  "talk_url": null,
                  "texts": {
                    "review_ask": "Would you leave a short review on {{platform}}? No reward, just your honest opinion.",
                    "review_cta": "Leave a review",
                    "title": "Before you go",
                    "reason_prompt": "Why are you cancelling?",
                    "other_placeholder": "Tell us more (optional)",
                    "offer_title": "One last thing",
                    "accept": "Accept the offer",
                    "decline": "No thanks, continue",
                    "feedback_prompt": "Anything we could have done better?",
                    "feedback_submit": "Continue",
                    "confirm_title": "Cancel your subscription?",
                    "confirm_body": "You keep access until the end of the paid period.",
                    "cancel_now": "Cancel my subscription",
                    "saved_title": "Thanks for staying!",
                    "cancelled_title": "Your subscription is cancelled",
                    "cancelled_body": "You keep access until the end of the paid period.",
                    "offer_discount": "{{percent}}% off for {{months}} months",
                    "offer_pause": "Pause for {{months}} months, free",
                    "offer_downgrade": "Switch to {{plan}}",
                    "offer_talk": "Talk to the team",
                    "choose_subscription_title": "Which subscription?",
                    "choose_subscription_body": "You have several subscriptions. Choose the one you want to cancel.",
                    "choose_subscription_select": "Continue with this one",
                    "cancel_this_one": "Cancel this one",
                    "renews_on": "Renews on {{date}}"
                  },
                  "feedback_enabled": true,
                  "feedback": {
                    "enabled": true,
                    "required": false
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Acme",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "cancel_url_now": null,
                  "cancel_status": null,
                  "cancel_effective_at": null,
                  "result": null,
                  "access_until": null,
                  "confirmation_email_sent": false,
                  "review": null,
                  "expires_at": "2026-11-05T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelSession"
                }
              }
            }
          },
          "404": {
            "description": "a session of another customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/cancel_sessions/{id}/answer": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "X-NoChurn-Customer-Token",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "Origin",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Answer the current step",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "answerCancelSession",
        "description": "One body per step: choose_subscription → { subscription } (one of the session's subscriptions, DECISIONS 72); reason → { reason, comment? } (comment for a free-text reason); offer → { accept_offer }; feedback → { feedback? } (required when feedback.required). Cancelling never needs an answer: POST /cancel.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "the next step, with the offer in the account's words",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_session",
                  "id": "ses_gXwed1ERtHMt3LtB",
                  "livemode": false,
                  "step": "offer",
                  "outcome": null,
                  "locale": "fr",
                  "holdout": false,
                  "published": true,
                  "customer": {
                    "id": "cst_SS392NRTjKfXKLRS",
                    "stripe_customer_id": "cus_1",
                    "external_id": "user_42"
                  },
                  "subscription": {
                    "id": "nsub_UXXWSnvbbahnokAE",
                    "plan": null,
                    "status": "active",
                    "amount_cents": 4900,
                    "currency": "eur",
                    "interval": "month",
                    "interval_count": 1,
                    "current_period_end": "2026-11-24T10:00:00Z",
                    "cancel_at_period_end": false
                  },
                  "subscriptions": [],
                  "reasons": [
                    {
                      "key": "too_expensive",
                      "label": "C'est trop cher",
                      "free_text": false
                    },
                    {
                      "key": "missing_feature",
                      "label": "Il manque une fonctionnalité",
                      "free_text": false
                    },
                    {
                      "key": "not_using",
                      "label": "Je ne l'utilise pas assez",
                      "free_text": false
                    },
                    {
                      "key": "switching",
                      "label": "Je passe à un autre outil",
                      "free_text": false
                    },
                    {
                      "key": "temporary",
                      "label": "J'ai juste besoin d'une pause",
                      "free_text": false
                    },
                    {
                      "key": "project_finished",
                      "label": "Mon projet est terminé",
                      "free_text": false
                    },
                    {
                      "key": "other",
                      "label": "Autre chose",
                      "free_text": true
                    }
                  ],
                  "offer": {
                    "id": "off_A8q4hT3LvQy4tvDX",
                    "kind": "discount",
                    "title": "30 % de moins, 3 mois : restez !",
                    "url": null,
                    "percent": 30,
                    "months": 3,
                    "plan_name": null
                  },
                  "talk_url": null,
                  "texts": {
                    "review_ask": "Laisseriez-vous un court avis sur {{platform}} ? Aucune récompense, juste votre avis sincère.",
                    "review_cta": "Laisser un avis",
                    "title": "Avant de partir",
                    "reason_prompt": "Pourquoi résiliez-vous ?",
                    "other_placeholder": "Dites-nous en plus (facultatif)",
                    "offer_title": "Une dernière chose",
                    "accept": "Accepter l'offre",
                    "decline": "Non merci, continuer",
                    "feedback_prompt": "Qu'aurions-nous pu faire mieux ?",
                    "feedback_submit": "Continuer",
                    "confirm_title": "Résilier votre abonnement ?",
                    "confirm_body": "Vous gardez l'accès jusqu'à la fin de la période payée.",
                    "cancel_now": "Résilier mon abonnement",
                    "saved_title": "Merci de rester !",
                    "cancelled_title": "Votre abonnement est résilié",
                    "cancelled_body": "Vous gardez l'accès jusqu'à la fin de la période payée.",
                    "offer_discount": "{{percent}} % de moins, {{months}} mois : restez !",
                    "offer_pause": "Mettre en pause {{months}} mois, gratuitement",
                    "offer_downgrade": "Passer à {{plan}}",
                    "offer_talk": "Parler à l'équipe",
                    "choose_subscription_title": "Quel abonnement ?",
                    "choose_subscription_body": "Vous avez plusieurs abonnements. Choisissez celui que vous voulez résilier.",
                    "choose_subscription_select": "Continuer avec celui-ci",
                    "cancel_this_one": "Résilier celui-ci",
                    "renews_on": "Renouvellement le {{date}}"
                  },
                  "feedback_enabled": true,
                  "feedback": {
                    "enabled": true,
                    "required": false
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Acme",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "cancel_url_now": null,
                  "cancel_status": null,
                  "cancel_effective_at": null,
                  "result": null,
                  "access_until": null,
                  "confirmation_email_sent": false,
                  "review": null,
                  "expires_at": "2026-11-05T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelSession"
                }
              }
            }
          },
          "400": {
            "description": "a required feedback left empty",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "choose_subscription: not one of the customer's live subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscription": {
                    "type": "string",
                    "example": "nsub_7Hd2kQ9xLm3VbN4p",
                    "description": "choose_subscription step: one of the customer's live subscriptions (nsub_…, sub_… or its id in your billing)."
                  },
                  "reason": {
                    "type": "string",
                    "example": "too_expensive",
                    "description": "Reason step: one of the session's reasons[].key."
                  },
                  "comment": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Reason step: free text (reasons with free_text)."
                  },
                  "accept_offer": {
                    "type": "boolean",
                    "description": "Offer step: true keeps the subscription with the offer (talk: talk_url, the session goes on)."
                  },
                  "feedback": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Feedback step: optional unless feedback.required."
                  }
                }
              },
              "example": {
                "reason": "too_expensive"
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/cancel_sessions/{id}/cancel": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "X-NoChurn-Customer-Token",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "Origin",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "summary": "Cancel now (click to cancel, at any step)",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "cancelCancelSession",
        "description": "Works at every step, after expiry, for the holdout, an unpublished flow and an unknown customer. Idempotent: a retry never cancels twice. cancel_status: done (Stripe), requested (your billing, billing.cancel_requested), pending (NoChurn retries until it is done). At choose_subscription (several live subscriptions, none chosen) the body names the one to cancel; without it, 422 subscription_required: never a guess, never all of them (DECISIONS 72).",
        "parameters": [],
        "responses": {
          "200": {
            "description": "cancelled",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_session",
                  "id": "ses_GMLvwQHvtdEcp5DZ",
                  "livemode": false,
                  "step": "done",
                  "outcome": "cancelled",
                  "locale": "en",
                  "holdout": false,
                  "published": true,
                  "customer": {
                    "id": "cst_Jy3PTUaA6DX1CQc3",
                    "stripe_customer_id": "cus_1",
                    "external_id": "user_42"
                  },
                  "subscription": {
                    "id": "nsub_MstpgaamkomcxhiZ",
                    "plan": null,
                    "status": "active",
                    "amount_cents": 4900,
                    "currency": "eur",
                    "interval": "month",
                    "interval_count": 1,
                    "current_period_end": "2026-11-24T10:00:00Z",
                    "cancel_at_period_end": true
                  },
                  "subscriptions": [],
                  "reasons": [
                    {
                      "key": "too_expensive",
                      "label": "It's too expensive",
                      "free_text": false
                    },
                    {
                      "key": "missing_feature",
                      "label": "A feature is missing",
                      "free_text": false
                    },
                    {
                      "key": "not_using",
                      "label": "I don't use it enough",
                      "free_text": false
                    },
                    {
                      "key": "switching",
                      "label": "I'm switching to another tool",
                      "free_text": false
                    },
                    {
                      "key": "temporary",
                      "label": "I only need a break",
                      "free_text": false
                    },
                    {
                      "key": "project_finished",
                      "label": "My project is finished",
                      "free_text": false
                    },
                    {
                      "key": "other",
                      "label": "Something else",
                      "free_text": true
                    }
                  ],
                  "offer": null,
                  "talk_url": null,
                  "texts": {
                    "review_ask": "Would you leave a short review on {{platform}}? No reward, just your honest opinion.",
                    "review_cta": "Leave a review",
                    "title": "Before you go",
                    "reason_prompt": "Why are you cancelling?",
                    "other_placeholder": "Tell us more (optional)",
                    "offer_title": "One last thing",
                    "accept": "Accept the offer",
                    "decline": "No thanks, continue",
                    "feedback_prompt": "Anything we could have done better?",
                    "feedback_submit": "Continue",
                    "confirm_title": "Cancel your subscription?",
                    "confirm_body": "You keep access until the end of the paid period.",
                    "cancel_now": "Cancel my subscription",
                    "saved_title": "Thanks for staying!",
                    "cancelled_title": "Your subscription is cancelled",
                    "cancelled_body": "You keep access until the end of the paid period.",
                    "offer_discount": "{{percent}}% off for {{months}} months",
                    "offer_pause": "Pause for {{months}} months, free",
                    "offer_downgrade": "Switch to {{plan}}",
                    "offer_talk": "Talk to the team",
                    "choose_subscription_title": "Which subscription?",
                    "choose_subscription_body": "You have several subscriptions. Choose the one you want to cancel.",
                    "choose_subscription_select": "Continue with this one",
                    "cancel_this_one": "Cancel this one",
                    "renews_on": "Renews on {{date}}"
                  },
                  "feedback_enabled": true,
                  "feedback": {
                    "enabled": true,
                    "required": false
                  },
                  "theme": {
                    "object": "cancel_flow_theme",
                    "preset": "light",
                    "format": "modal",
                    "tokens": {
                      "bg": "#f5f6fa",
                      "surface": "#ffffff",
                      "text": "#1c1e4a",
                      "muted": "#686b85",
                      "accent": "#4637d9",
                      "accent_text": "#ffffff",
                      "border": "#e3e5ec",
                      "danger": "#c2410c",
                      "font": "system-ui, sans-serif",
                      "font_size": 15,
                      "radius": 12,
                      "radius_control": 8,
                      "overlay": 0.5,
                      "max_width": 480,
                      "shadow": "md",
                      "spacing": "regular",
                      "border_width": 1,
                      "border_style": "solid",
                      "font_scale": 1.25,
                      "font_weight_title": 600
                    },
                    "font_url": null,
                    "brand": "logo",
                    "logo_url": null,
                    "app_name": "Acme",
                    "end_screen": {
                      "style": "celebrate",
                      "auto_close_seconds": 6
                    },
                    "custom_css": "",
                    "cancel_button_check": "ok",
                    "classes": [
                      "nc-modal",
                      "nc-title",
                      "nc-reason",
                      "nc-offer",
                      "nc-btn-primary",
                      "nc-btn-cancel",
                      "nc-footer"
                    ]
                  },
                  "cancel_url_now": null,
                  "cancel_status": "done",
                  "cancel_effective_at": "2026-11-24T10:00:00Z",
                  "result": null,
                  "access_until": "2026-11-24T10:00:00Z",
                  "confirmation_email_sent": false,
                  "review": null,
                  "expires_at": "2026-11-05T10:00:00Z",
                  "created_at": "2026-11-04T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelSession"
                }
              }
            }
          },
          "422": {
            "description": "several live subscriptions and none named",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscription": {
                    "type": "string",
                    "description": "choose_subscription step: the one to cancel (nsub_…, sub_… or its id in your billing)."
                  }
                }
              },
              "example": {}
            }
          }
        }
      }
    },
    "/v1/cancel_flows/{id}/theme": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "description": "default or flw_…",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "The cancel modal's design (tokens, format, logo, CSS)",
        "tags": [
          "Cancel flow"
        ],
        "operationId": "getCancelFlowTheme",
        "description": "The published version of the key's mode. Every token is present (preset, then the account's). For native apps and the widget; also inside each cancel session (with its segment / A/B design).",
        "responses": {
          "200": {
            "description": "the theme",
            "content": {
              "application/json": {
                "example": {
                  "object": "cancel_flow_theme",
                  "preset": "dark",
                  "format": "panel",
                  "tokens": {
                    "bg": "#050505",
                    "surface": "#0b0b0c",
                    "text": "#ededed",
                    "muted": "#9b9ba1",
                    "accent": "#ededed",
                    "accent_text": "#050505",
                    "border": "#2a2a2e",
                    "danger": "#f97316",
                    "font": "Inter, sans-serif",
                    "font_size": 15,
                    "radius": 12,
                    "radius_control": 8,
                    "overlay": 0.6,
                    "max_width": 480,
                    "shadow": "none",
                    "spacing": "compact",
                    "border_width": 2,
                    "border_style": "dashed",
                    "font_scale": 1.5,
                    "font_weight_title": 500
                  },
                  "font_url": "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
                  "brand": "logo",
                  "logo_url": "https://cdn.acme.com/logo.svg",
                  "app_name": "Acme",
                  "end_screen": {
                    "style": "celebrate",
                    "auto_close_seconds": 6
                  },
                  "custom_css": "",
                  "cancel_button_check": "ok",
                  "classes": [
                    "nc-modal",
                    "nc-title",
                    "nc-reason",
                    "nc-offer",
                    "nc-btn-primary",
                    "nc-btn-cancel",
                    "nc-footer"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/CancelFlowTheme"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/widget/ping": {
      "post": {
        "summary": "The widget loaded (onboarding: detected)",
        "tags": [
          "Widget"
        ],
        "operationId": "pingWidget",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Publishable key + allowed Origin, no customer token. The first ping marks the widget as detected (Event widget.detected). Returns the key's fallback cancel link (DECISIONS 71).",
        "responses": {
          "200": {
            "description": "detected",
            "content": {
              "application/json": {
                "example": {
                  "object": "widget_ping",
                  "detected_at": "2026-11-04T10:00:00Z",
                  "cancel_fallback_url": "http://localhost:3000/k/pk_test_cuzA5wx4ZZPE357jiSJReKBsj1iaeavi"
                },
                "schema": {
                  "$ref": "#/components/schemas/WidgetPing"
                }
              }
            }
          }
        },
        "x-audience": "browser"
      }
    },
    "/v1/widget/guard_report": {
      "post": {
        "summary": "The widget had to restore the hidden cancel button",
        "tags": [
          "Widget"
        ],
        "operationId": "reportWidgetGuard",
        "description": "Sent by the modal when its render-time check (click to cancel) found the cancel button hidden and repaired it. Publishable key only + customer token + allowed Origin. Same alert as the server-side CSS check: Event cancel_flow.css_fallback_applied (data.source widget), at most one per flow and mode per hour, and a To do item until the next publish. Limits: 10 reports a minute per key and customer, 60 per IP (429).",
        "parameters": [
          {
            "name": "X-NoChurn-Customer-Token",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "recorded once an hour",
            "content": {
              "application/json": {
                "example": {
                  "object": "widget_guard_report",
                  "recorded": true,
                  "event": "nevt_aMee2SMCB7o7TYT8"
                },
                "schema": {
                  "$ref": "#/components/schemas/WidgetGuardReport"
                }
              }
            }
          },
          "400": {
            "description": "unknown problems",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "no customer token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "a secret key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "problems"
                ],
                "properties": {
                  "problems": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "display",
                        "visibility",
                        "opacity",
                        "pointer-events",
                        "contrast",
                        "size",
                        "font-size",
                        "covered"
                      ]
                    },
                    "description": "What the guard repaired."
                  },
                  "session": {
                    "type": "string",
                    "description": "The cancel session shown (ses_…), if any."
                  },
                  "css_from_code": {
                    "type": "boolean",
                    "description": "A theme or css was passed in code (NoChurn.cancel({ css }))."
                  }
                }
              },
              "example": {
                "problems": [
                  "display",
                  "contrast"
                ],
                "session": "ses_jiTb8PYHFcUhWCni",
                "css_from_code": true
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/widget/events": {
      "post": {
        "summary": "Track usage events from the browser (page_view, NoChurn.track)",
        "tags": [
          "Widget"
        ],
        "operationId": "createWidgetEvents",
        "description": "Called by js.nochurn.sh/v1 for your logged-in end user: page_view (path only, never the query string) and NoChurn.track(name, props). Publishable key only, in the Authorization header or as `key` in the body (navigator.sendBeacon cannot set headers); a secret key is refused (403 publishable_key_required). The customer is read from the signed widget token (`token`, HS256 { kind, value, exp }), never from the events. The body is JSON, as application/json or text/plain (a beacon, no preflight), 256 KB at most. 50 events per call. name: 1 to 64 of a-z 0-9 _ . : -; properties: flat, 20 keys, strings of 256 characters, numbers, booleans or null, 4 KB; timestamp: ISO 8601, clamped to the last 24 hours (the browser clock is not trusted); idempotency_key required (64 characters), deduplicated per mode. Errors are per event (rejected); an unknown customer rejects every event with customer_not_found. Limits: 120 events a minute per key and customer, 300 requests a minute per IP (429 rate_limited, retry_after). Neither the IP nor the user agent is stored.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "a beacon: text/plain body, key in the body, no Authorization header",
            "content": {
              "application/json": {
                "example": {
                  "object": "widget_event_batch",
                  "accepted": 1,
                  "duplicates": 0,
                  "rejected": [
                    {
                      "index": 1,
                      "code": "invalid_request",
                      "message": "name \"Export Used\" is not snake_case."
                    },
                    {
                      "index": 2,
                      "code": "invalid_request",
                      "message": "properties: \"nested\" is not a string, number, boolean or null."
                    },
                    {
                      "index": 3,
                      "code": "invalid_request",
                      "message": "idempotency_key is required (64 characters at most)."
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/WidgetEventBatch"
                }
              }
            }
          },
          "400": {
            "description": "a body that is not JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "no widget token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "a secret key (never in a web page)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "over 120 events a minute for this key and customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-audience": "browser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "events"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Your publishable key, when the Authorization header cannot be sent (sendBeacon).",
                    "example": "pk_test_…"
                  },
                  "token": {
                    "type": "string",
                    "description": "The widget token signed by your server (HS256 { kind, value, exp }), as for NoChurn.init."
                  },
                  "client": {
                    "type": "string",
                    "example": "widget/1.0.0",
                    "description": "The script's version, when the NoChurn-Client header cannot be sent (sendBeacon): GET /v1/account/clients."
                  },
                  "events": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "idempotency_key"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "pattern": "^[a-z0-9_.:-]{1,64}$",
                          "example": "page_view"
                        },
                        "properties": {
                          "type": "object",
                          "maxProperties": 20,
                          "description": "Flat: strings (256), numbers, booleans or null; 20 keys, 4 KB.",
                          "additionalProperties": {
                            "nullable": true,
                            "oneOf": [
                              {
                                "type": "string",
                                "maxLength": 256
                              },
                              {
                                "type": "number"
                              },
                              {
                                "type": "boolean"
                              }
                            ]
                          }
                        },
                        "timestamp": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Clamped to [now - 24 h, now]."
                        },
                        "idempotency_key": {
                          "type": "string",
                          "maxLength": 64
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "token": "EXAMPLE00000000000000000",
                "events": [
                  {
                    "name": "page_view",
                    "properties": {
                      "path": "/billing"
                    },
                    "timestamp": "2026-11-01T10:00:00Z",
                    "idempotency_key": "e1"
                  },
                  {
                    "name": "Export Used",
                    "idempotency_key": "e2"
                  },
                  {
                    "name": "export:csv",
                    "properties": {
                      "rows": 12,
                      "nested": {
                        "a": 1
                      }
                    },
                    "idempotency_key": "e3"
                  },
                  {
                    "name": "login"
                  }
                ]
              }
            },
            "text/plain": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "events"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Your publishable key, when the Authorization header cannot be sent (sendBeacon).",
                    "example": "pk_test_…"
                  },
                  "token": {
                    "type": "string",
                    "description": "The widget token signed by your server (HS256 { kind, value, exp }), as for NoChurn.init."
                  },
                  "client": {
                    "type": "string",
                    "example": "widget/1.0.0",
                    "description": "The script's version, when the NoChurn-Client header cannot be sent (sendBeacon): GET /v1/account/clients."
                  },
                  "events": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "idempotency_key"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "pattern": "^[a-z0-9_.:-]{1,64}$",
                          "example": "page_view"
                        },
                        "properties": {
                          "type": "object",
                          "maxProperties": 20,
                          "description": "Flat: strings (256), numbers, booleans or null; 20 keys, 4 KB.",
                          "additionalProperties": {
                            "nullable": true,
                            "oneOf": [
                              {
                                "type": "string",
                                "maxLength": 256
                              },
                              {
                                "type": "number"
                              },
                              {
                                "type": "boolean"
                              }
                            ]
                          }
                        },
                        "timestamp": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Clamped to [now - 24 h, now]."
                        },
                        "idempotency_key": {
                          "type": "string",
                          "maxLength": 64
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "required": true
        }
      }
    },
    "/v1/account/widget_secret": {
      "get": {
        "summary": "Is the widget signing secret set?",
        "tags": [
          "Developers"
        ],
        "operationId": "getWidgetSecret",
        "responses": {
          "200": {
            "description": "never shows the secret",
            "content": {
              "application/json": {
                "example": {
                  "object": "widget_secret",
                  "configured": false,
                  "created_at": null,
                  "secret": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WidgetSecret"
                }
              }
            }
          }
        },
        "x-audience": "public"
      },
      "post": {
        "summary": "Create or rotate the widget signing secret",
        "tags": [
          "Developers"
        ],
        "operationId": "rotateWidgetSecret",
        "description": "Your server signs customer tokens with it (HS256). Shown once; rotating invalidates tokens signed with the old one.",
        "responses": {
          "201": {
            "description": "shown once",
            "content": {
              "application/json": {
                "example": {
                  "object": "widget_secret",
                  "configured": true,
                  "created_at": "2026-11-04T10:00:00Z",
                  "secret": "ncws_EXAMPLE00000000000000000"
                },
                "schema": {
                  "$ref": "#/components/schemas/WidgetSecret"
                }
              }
            }
          }
        },
        "x-audience": "public"
      }
    }
  }
}