> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nautilus.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Cancellation survey

> The reasons your members select in the portal cancellation flow — the member-stated "why" behind churn (distinct from the POS churn *mechanism* in [Cancellations](/api-reference/endpoint/cancellations)).

Labels are normalized into stable categories; `rawCodes` preserves the underlying codes. The **Other** bucket includes free-text samples. `totals.selections` counts reason selections, a superset of completed cancellations. Works for any organization; requires a tenant-scoped API key.

<RequestExample>
  ```bash All-time theme={null}
  curl "https://app.nautilus.co/api/v1/cancellation-survey" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</RequestExample>

## Response

* **`totals.selections`** — the number of reason selections. This is a **superset** of completed cancellations: some members select a reason and then accept a retention offer and stay. Use the relative distribution as the reliable signal.
* **`reasons`** — normalized reason labels with their share. Synonymous source codes are folded into one label; `rawCodes` lists each underlying code with its own count, so the granularity inside a bucket isn't lost.
* **`other`** — the **Other** bucket, with free-text samples members typed. `freeTextAvailable: false` means the free-text lookup was temporarily unavailable (unknown), not that there is none.

## Notes

This is the member-stated reason (the "why"), distinct from the POS-derived churn mechanism in [Cancellations](/api-reference/endpoint/cancellations). It is provider-agnostic and works for any organization whose customers use the portal cancellation flow.

## Errors

* **401** — missing or invalid API key.


## OpenAPI

````yaml GET /api/v1/cancellation-survey
openapi: 3.1.0
info:
  title: Nautilus Public API
  version: 1.0.0
  description: >-
    Tenant-scoped public API. Except for /api/v1/openapi.json, requests use a
    Clerk tenant API key as a Bearer token. The key's organizationId is the sole
    tenant scope; organization IDs supplied by callers are never accepted.
    Detail endpoints deliberately return 404 for both missing and cross-tenant
    records. Canonical collections use the standard envelope and endpoint-bound
    cursor pagination. Timestamps are UTC ISO 8601 values and monetary amounts
    ending in Cents are integer cents. Responses use narrow serializers and
    exclude credentials, API keys, provider configuration, access tokens,
    webhook secrets, raw provider payloads, and internal workflow or activity
    data.
servers:
  - url: https://app.nautilus.co
security:
  - tenantApiKey: []
tags:
  - name: Contract
  - name: Cancellations
  - name: Sites
  - name: Products
  - name: Checkout links
  - name: Purchases
  - name: Contact lists
  - name: Contacts
  - name: Marketing
  - name: Forms
  - name: Vouchers
  - name: Links
  - name: Automations
  - name: Surveys
  - name: Cases
  - name: Sonny's POS
  - name: Analytics
  - name: Messaging
  - name: Legacy
    description: Deprecated noncanonical endpoints retained for compatibility.
paths:
  /api/v1/cancellation-survey:
    get:
      tags:
        - Surveys
        - Legacy
      summary: Cancellation survey
      description: >-
        The reasons your members select in the portal cancellation flow — the
        member-stated "why" behind churn (distinct from the POS churn
        *mechanism* in [Cancellations](/api-reference/endpoint/cancellations)).


        Labels are normalized into stable categories; `rawCodes` preserves the
        underlying codes. The **Other** bucket includes free-text samples.
        `totals.selections` counts reason selections, a superset of completed
        cancellations. Works for any organization; requires a tenant-scoped API
        key.
      operationId: getCancellationSurvey
      parameters:
        - name: start
          in: query
          required: false
          description: >-
            Inclusive lower bound on the date (ISO 8601 date or datetime).
            Invalid values are ignored. Omit for all-time.
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          required: false
          description: >-
            Inclusive upper bound on the date (ISO 8601 date or datetime).
            Invalid values are ignored. Omit for all-time.
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: >-
            Member-stated cancellation reasons for the tenant-scoped
            organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancellationSurveyResponse'
              example:
                organizationId: org_2abcXYZ123
                dateRange:
                  start: null
                  end: null
                source: exit_survey
                available: true
                totals:
                  selections: 2658
                reasons:
                  - reason: Too expensive
                    rawCodes:
                      - code: too_expensive
                        count: 402
                      - code: too-expensive
                        count: 247
                      - code: it_s_too_expensive
                        count: 111
                    count: 760
                    pct: 28.6
                  - reason: Not using it enough
                    rawCodes:
                      - code: not_using_enough
                        count: 318
                      - code: i_don_t_wash_my_car_enough
                        count: 173
                    count: 491
                    pct: 18.5
                  - reason: Other
                    rawCodes:
                      - code: other
                        count: 379
                      - code: other_reason
                        count: 49
                    count: 428
                    pct: 16.1
                other:
                  count: 428
                  pct: 16.1
                  freeTextAvailable: true
                  withFreeText: 48
                  samples:
                    - text: Moving out of state
                      count: 3
                    - text: Joining my spouse's plan
                      count: 2
        '401':
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      deprecated: true
components:
  schemas:
    CancellationSurveyResponse:
      type: object
      title: Cancellation survey
      required:
        - organizationId
        - dateRange
        - source
        - available
        - totals
        - reasons
        - other
      properties:
        organizationId:
          type: string
        dateRange:
          $ref: '#/components/schemas/DateRange'
        source:
          type: string
          enum:
            - exit_survey
        available:
          type: boolean
          description: False only when the analytics source is unavailable after retries.
        totals:
          type: object
          required:
            - selections
          properties:
            selections:
              type: integer
              description: Reason selections (a superset of completed cancellations).
        reasons:
          type: array
          items:
            $ref: '#/components/schemas/SurveyReasonRow'
        other:
          $ref: '#/components/schemas/SurveyOther'
    Error:
      type: object
      additionalProperties: false
      properties:
        error:
          type: string
      required:
        - error
    DateRange:
      type: object
      title: Date range
      required:
        - start
        - end
      properties:
        start:
          type:
            - string
            - 'null'
          format: date-time
          description: Parsed `start` filter, or null when omitted.
        end:
          type:
            - string
            - 'null'
          format: date-time
          description: Parsed `end` filter, or null when omitted.
    SurveyReasonRow:
      type: object
      title: Survey reason row
      required:
        - reason
        - rawCodes
        - count
        - pct
      properties:
        reason:
          type: string
          description: Normalized display label (synonym codes folded into one).
        rawCodes:
          type: array
          description: >-
            Underlying source codes folded into this label, each with its own
            count.
          items:
            type: object
            required:
              - code
              - count
            properties:
              code:
                type: string
              count:
                type: integer
        count:
          type: integer
        pct:
          type: number
    SurveyOther:
      type:
        - object
        - 'null'
      title: Other breakout
      required:
        - count
        - pct
        - freeTextAvailable
        - withFreeText
        - samples
      properties:
        count:
          type: integer
        pct:
          type: number
        freeTextAvailable:
          type: boolean
          description: >-
            False means the free-text sub-query was unavailable (unknown), not
            that there is none.
        withFreeText:
          type: integer
        samples:
          type: array
          items:
            type: object
            properties:
              text:
                type: string
              count:
                type: integer
  securitySchemes:
    tenantApiKey:
      type: http
      scheme: bearer
      bearerFormat: Clerk tenant API key
      description: >-
        Clerk tenant API key. The organizationId encoded by the key is the exact
        and only tenant scope.

````