openapi: 3.1.0
info:
  title: ScribeUp Technical Documentation
  description: |-
    **ScribeUp Subscription Manager** is an embeddable tool that helps your users track, manage, and optimize their recurring subscriptions and bills, directly inside your app. It's designed to boost user engagement, improve card primacy, and surface insights you can act on.

    By integrating Subscription Manager, your users can:
    - View all recurring charges (on internal + external accounts) in one dashboard.
    - Cancel unwanted subscriptions or bills.
    - Switch payment methods to your institution’s card.
    - Sync renewals to their personal calendar.
    - Receive alerts about upcoming renewals.

    This is delivered as a secure, in-app experience via a web iframe or native mobile SDK, giving your users powerful subscription management tools without leaving your product.

    # Integration Overview

    The core components of integration include:
    - **[Authentication](#tag/Authentication)**: Simple API call or SAML/SSO for secure user login
    - **[UI Integration](#tag/UI-Integration)**: Embed the Subscription Manager UI using iframe or SDK both as a full-page embedded experience or a preview widget.
    - **[Transaction Integration](#tag/Transactions-Integration)**: Connect to your Transactions API for internal charges

    Additional integrations to optimize the experience:
    - **[Plaid Integration](#tag/Plaid-Integration)** _(optional)_: Use your existing Plaid integration to enable users to link external accounts and identify recurring charges
    - **[Card Data Integration](#tag/Card-Data-Integration)** _(optional)_: Securely pass card details to enable users to easily switch payment methods to your card
    - **[Notification Webhook](#tag/Notification-Webhook)** _(optional)_: Send real-time alerts for renewals, bills, and insights
    - **[Reporting API](#tag/Reporting)** _(optional)_: Access user-level insights and export subscription and behavior data into your systems

  contact:
    email: dev@scribeup.io
    url: 'https://docs.scribeup.io'
  x-logo:
    url: 'logo.png'
    altText: ScribeUp Logo
servers:
  - url: 'https://staging.api.scribeup.co'
paths:
  /api/v1/auth/users/init:
    post:
      tags:
        - Authentication
      summary: API Key based authentication
      description: |-

        General flow:
          - Client server calls ScribeUp `/users/init` API after verifying user login
          - API returns url with temporary authorization token
          - Client embeds iframe or initializes native mobile SDK using returned url
          - **NOTE**: The URL includes a secure token valid for 60 minutes. Once rendered, it’s exchanged for a longer-lived session token that is refreshed automatically. For best results, fetch the URL just before rendering

      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: string
                  description: Client's unique identifier for user
                email:
                  type: string
                  description: User's email address
                first_name:
                  type: string
                  description: User's first name
                last_name:
                  type: string
                  description: User's last name
                org_id:
                  type: string
                  description: Optional organization ID. Used for multi-tennant clients
                secondary_user_id:
                  type: string
                  description: Optional secondary user identifier
                section:
                  type: string
                  enum:
                    - calendar
                    - list
                    - accounts
                  description: >
                    Optional section within the app where the user should land (e.g., "calendar" to open their calendar view)
                metadata:
                  type: object
                  additionalProperties:
                    type: string
                  description: Optional metadata object for additional context (e.g., experiment variants, user attributes)
                  example: {"experiment_variant": "2", "premium_user": true}
              required:
                - user_id
      parameters:
        - in: header
          name: Authorization
          required: true
          schema:
            type: string
            example: Bearer {API_TOKEN}
          description: Bearer token for authorization
        - in: header
          name: X-Client-ID
          required: true
          schema:
            type: string
            example: "{CLIENT_ID}"
          description: Issued Client ID
      responses:
        '200':
          description: Successfully authenticated user
          content:
            application/json:
              schema:
                type: object
                properties:
                  url:
                    type: string
                    description: Main URL with temporary authorization token (60min) to embed in iframe
                    example: 'http://staging.widget.scribeup.io/preview#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicHJldmlldyIsImV4cCI6MTcxODM5MjMwNCwiaWF0IjoxNzE4MzkyMjQ0LCJqdGkiOiI5OGYwYmI4MTVlMGE0YzM1YWZiNzc5ZTQxMzI3MGI3ZiIsInVzZXJfaWQiOiJhMjg3MWJjMi03M2Q3LTRjODAtYWI1NC0yMWE4MmEwNjBjZTEifQ.OV7VPUHrzpJv3ky_hvw46MDE9wJzNmiji8VFhH9fQlM'
                  calendar_preview_url:
                    type: string
                    description: URL for calendar view preview widget
                    example: 'http://staging.widget.scribeup.io/calendar-preview#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicHJldmlldyIsImV4cCI6MTcxODM5MjMwNCwiaWF0IjoxNzE4MzkyMjQ0LCJqdGkiOiI5OGYwYmI4MTVlMGE0YzM1YWZiNzc5ZTQxMzI3MGI3ZiIsInVzZXJfaWQiOiJhMjg3MWJjMi03M2Q3LTRjODAtYWI1NC0yMWE4MmEwNjBjZTEifQ.OV7VPUHrzpJv3ky_hvw46MDE9wJzNmiji8VFhH9fQlM'
                  list_preview_url:
                    type: string
                    description: URL for list view preview widget
                    example: 'http://staging.widget.scribeup.io/list-preview#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicHJldmlldyIsImV4cCI6MTcxODM5MjMwNCwiaWF0IjoxNzE4MzkyMjQ0LCJqdGkiOiI5OGYwYmI4MTVlMGE0YzM1YWZiNzc5ZTQxMzI3MGI3ZiIsInVzZXJfaWQiOiJhMjg3MWJjMi03M2Q3LTRjODAtYWI1NC0yMWE4MmEwNjBjZTEifQ.OV7VPUHrzpJv3ky_hvw46MDE9wJzNmiji8VFhH9fQlM'
        '400':
          description: Bad request
        '403':
          description: Authentication failed

  /saml2/acs:
    post:
      tags:
        - Authentication
      summary: SAML ACS Endpoint
      description: |-
        Handles the SAML response from the Identity Provider (IdP).

        ![SAML Diagram](saml_flow.png)

        ### SAML Authentication Steps

        1. User logs into FI's website or mobile app.
        2. Digital Banking Platform (IdP) sends ScribeUp (SP) a SAML assertion.
        3. ScribeUp validates SAML, returns an embeddable iframe URL.

        Please reach out to your contact at ScribeUp to exchange Identity Provider (IdP) and Service Provider (SP) metadata.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                SAMLResponse:
                  type: string
                  description: |-
                    The SAML response from the Identity Provider, Base64 encoded.  \
                    SAML Assertion Attributes:
                    - `FI_ID`: ID of the financial institution. (required)
                    - `USER_ID`: Unique user ID. (required)
                    - `USER_EMAIL`: User's email address. (required)
                    - `USER_ACCOUNTS`: List and details of user's accounts (optional)
              required:
                - SAMLResponse
      responses:
        '302':
          description: Successfully authenticates and redirects to ScribeUp
        '403':
          description: Authentication failed

  /api/v1/report/users:
    get:
      summary: Get Users
      description: Retrieves a paginated list of users, including their current status, status history timestamps, and metadata.
      tags:
        - Reporting
      parameters:
        - in: header
          name: Authorization
          required: true
          schema:
            type: string
            example: Bearer {API_TOKEN}
          description: Bearer token for authorization
        - in: header
          name: X-Client-ID
          required: true
          schema:
            type: string
            example: "{CLIENT_ID}"
          description: Issued Client ID
        - in: query
          name: page
          required: false
          schema:
            type: integer
            minimum: 1
            example: 1
          description: Page number (default is 1)
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            example: 50
          description: Number of results per page (default is 50, max is 500)
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
                    description: Total number of users available
                  next:
                    type: string
                    format: uri
                    nullable: true
                    description: URL to the next page of results, if any
                  previous:
                    type: string
                    format: uri
                    nullable: true
                    description: URL to the previous page of results, if any
                  results:
                    type: array
                    description: List of users
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: ScribeUp ID (UUID)
                        external_id:
                          type: string
                          description: Client-provided unique ID
                        created:
                          type: string
                          format: date-time
                          description: Timestamp when the record was created
                        status:
                          type: string
                          enum:
                            - active
                            - inactive
                            - deactivated
                            - terminated
                          description: Current status of the user
                        last_updated:
                          type: string
                          format: date-time
                          description: Last time the record was updated
                        metadata:
                          type: object
                          description: Additional metadata for the user
                        activated_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: Timestamp when the user was activated
                        terminated_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: Timestamp when the user was terminated
                        deactivated_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: Timestamp when the user was deactivated
              examples:
                default_example:
                  value:
                    count: 2
                    next: null
                    previous: null
                    results:
                      - id: "cb87f41d-a692-4a5d-b395-2d08dd909dcd"
                        external_id: "fbfdb1f4-6b4e-4d41-9f06-391e06969ff7"
                        created: "2025-04-25T06:47:41.844976Z"
                        status: "active"
                        last_updated: "2025-04-25T06:55:57.115897Z"
                        metadata: null
                        activated_at: "2025-04-25T06:47:41.844976Z"
                        terminated_at: null
                        deactivated_at: null
                      - id: "65e3ab74-4cc0-4240-babf-a6956d7d780c"
                        external_id: "c2b7a91d-6a22-43f8-bb35-9e83f1a12f22"
                        created: "2025-04-15T10:22:12.009123Z"
                        status: "terminated"
                        last_updated: "2025-05-01T12:00:00.000000Z"
                        metadata: null
                        activated_at: "2025-04-15T10:22:12.009123Z"
                        terminated_at: "2025-05-01T12:00:00.000000Z"
                        deactivated_at: null
        '400':
          description: Bad request.
        '403':
          description: Forbidden.
        '404':
          description: Not found.

  /api/v1/report/users/{user_id}:
    get:
      summary: Get User
      description: Retrieves detailed information about a user, including their current status, status history, and subscription details.
      tags:
        - Reporting
      parameters:
        - in: header
          name: Authorization
          required: true
          schema:
            type: string
            example: Bearer {API_TOKEN}
          description: Bearer token for authorization
        - in: header
          name: X-Client-ID
          required: true
          schema:
            type: string
            example: "{CLIENT_ID}"
          description: Issued Client ID
        - in: path
          name: user_id
          required: true
          schema:
            type: string
          description: Client's unique identifier for the user
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: ScribeUp ID (UUID)
                  external_id:
                    type: string
                    description: Client-provided unique ID
                  created:
                    type: string
                    format: date-time
                    description: Timestamp when the record was created
                  status:
                    type: string
                    enum:
                      - active
                      - inactive
                      - deactivated
                      - terminated
                    description: Current status of the user
                  last_updated:
                    type: string
                    format: date-time
                    description: Last time the record was updated
                  metadata:
                    type: object
                    description: Additional metadata for the user
                  accounts:
                    type: array
                    description: List of linked accounts
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: Account ID (UUID)
                        mask:
                          type: string
                          description: Masked account number (e.g., last 4 digits)
                        type:
                          type: string
                          description: Account type
                        subtype:
                          type: string
                          description: Account subtype
                        name:
                          type: string
                          description: Account name
                        institution_name:
                          type: string
                          description: Name of financial institution
                  recurrences:
                    type: array
                    description: List of recurrence
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: Recurrence ID (UUID)
                        merchant:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              description: Merchant ID (UUID)
                            name:
                              type: string
                              description: Merchant name
                            url:
                              type: string
                              format: uri
                              description: Merchant website URL
                            logo_url:
                              type: string
                              format: uri
                              description: Merchant logo URL
                        frequency:
                          type: string
                          enum:
                            - weekly
                            - bi-weekly
                            - monthly
                            - bi-monthly
                            - quarterly
                            - semi-yearly
                            - yearly
                            - other
                          description: Payment frequency
                        type:
                          type: string
                          enum:
                            - subscription
                            - bill
                            - other
                          description: Recurrence type
                        status:
                          type: string
                          enum:
                            - active
                            - inactive
                            - canceled
                          description: Recurrence status
                        amount_cents:
                          type: integer
                          description: Amount in cents
                        monthly_amount_cents:
                          type: integer
                          description: Normalized monthly amount in cents
                        total_amount_cents:
                          type: integer
                          description: Total amount of detected payments in cents
                        next_payment_date:
                          type: string
                          format: date-time
                          description: Next scheduled payment date
                        last_payment_date:
                          type: string
                          format: date-time
                          description: Last payment date
                        account_id:
                          type: string
                          format: uuid
                          description: ID of the linked account (UUID)
                        loan_type:
                          type: string
                          nullable: true
                          enum:
                            - auto
                            - mortgage
                            - personal
                            - business
                            - student
                          description: Loan type, if applicable
              examples:
                default_example:
                  value:
                    id: "cb87f41d-a692-4a5d-b395-2d08dd909dcd"
                    external_id: "fbfdb1f4-6b4e-4d41-9f06-391e06969ff7"
                    created: "2025-04-25T06:47:41.844976Z"
                    status: "active"
                    last_updated: "2025-04-25T06:55:57.115897Z"
                    accounts:
                      - id: "65e3ab74-4cc0-4240-babf-a6956d7d780c"
                        mask: "1234"
                        type: "depository"
                        subtype: "checking"
                        name: "Daily Checking"
                        institution_name: "Discover"
                    recurrences:
                      - id: "ff04d458-c376-42d4-8cae-6ae5ccbf5dce"
                        merchant:
                          id: "12ce6a3d-618b-4cdc-a42e-500890e91847"
                          name: "Crunchyroll"
                          url: "https://crunchyroll.com"
                          logo_url: "https://dteas9sdy7vca.cloudfront.net/da01269a-dad7-38e8-b011-45f00d445785.png"
                        frequency: "monthly"
                        type: "subscription"
                        status: "active"
                        amount_cents: 999
                        monthly_amount_cents: 999
                        total_amount_cents: 2997
                        next_payment_date: "2025-05-18T06:57:08Z"
                        last_payment_date: "2025-04-18T06:57:08Z"
                        account_id: "65e3ab74-4cc0-4240-babf-a6956d7d780c"
                        loan_type: null
                      - id: "a8006635-f5ce-4863-819b-fa787eaa6746"
                        merchant:
                          id: "'62d3c139-1a6e-4902-8ea3-93b975cf2da0"
                          name: "GEICO"
                          url: "https://geico.com"
                          logo_url: "https://dteas9sdy7vca.cloudfront.net/11dd9262-e843-3024-97e0-05924c7c3e4e.png"
                        frequency: "yearly"
                        type: "bill"
                        status: "active"
                        amount_cents: 180000
                        monthly_amount_cents: 15000
                        total_amount_cents: 360000
                        next_payment_date: "2025-04-29T06:57:08Z"
                        last_payment_date: "2024-04-29T06:57:08Z"
                        account_id: "65e3ab74-4cc0-4240-babf-a6956d7d780c"
                        loan_type: null
                      - id: "b61c13e5-ef9c-4e3e-93d9-0baceb0a7a6a"
                        merchant:
                          id: "036372b2-0828-43bd-b03b-8dea312c08e5"
                          name: "DriveTime"
                          url: "https://drivetime.com"
                          logo_url: "https://dteas9sdy7vca.cloudfront.net/f29b67f7-c4cd-39b1-ba07-9f440e0d0e49.png"
                        frequency: "monthly"
                        type: "bill"
                        status: "active"
                        amount_cents: 25436
                        monthly_amount_cents: 25436
                        total_amount_cents: 152616
                        next_payment_date: "2025-05-27T22:28:26Z"
                        last_payment_date: "2025-04-27T22:28:26Z"
                        account_id: "65e3ab74-4cc0-4240-babf-a6956d7d780c"
                        loan_type: "auto"
        '400':
          description: Bad request.
        '403':
          description: Forbidden.
        '404':
          description: Not found.

  /api/v1/auth/users/{user_id}/status:
    put:
      tags:
        - Authentication
      summary: Update User Status
      description: |-
        Updates the status of a user.

        - **Terminated users**: This action is irreversible. Users will lose access to the ScribeUp platform.
        - **Deactivated users**: Users can reactivate themselves.

      parameters:
        - in: header
          name: Authorization
          required: true
          schema:
            type: string
            example: Bearer {API_TOKEN}
          description: Bearer token for authorization
        - in: header
          name: X-Client-ID
          required: true
          schema:
            type: string
            example: "{CLIENT_ID}"
          description: Issued Client ID
        - in: path
          name: user_id
          required: true
          schema:
            type: string
          description: Client's unique identifier for the user

      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [deactivate, terminate]
                  description: The action to be performed on the user status.
                reason:
                  type: string
                  description: Optional reason for the action.
              required:
                - action

      responses:
        '200':
          description: User status updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
        '400':
          description: Bad request.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    enum:
                      - Missing User ID
                      - Invalid action
                      - Invalid User
                      - User is already terminated
                      - User is not active
                    example: User is not active
        '403':
          description: Forbidden.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Invalid API token or Client ID

  /api/v1/webhook/external-accounts:
    post:
      tags:
        - Plaid Integration
      summary: Notify ScribeUp of Updated Plaid Accounts
      description: |-
        Clients use this API to inform ScribeUp about updates to a user's Plaid accounts, such as additions or removals.

        Upon receiving this notification, ScribeUp will call the Client's API to fetch updated processor tokens and apply the necessary changes.

      parameters:
        - in: header
          name: Authorization
          required: true
          schema:
            type: string
            example: Bearer {API_TOKEN}
          description: Bearer token for authorization
        - in: header
          name: X-Client-ID
          required: true
          schema:
            type: string
            example: "{CLIENT_ID}"
          description: Issued Client ID

      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: string
                  description: Client's unique identifier for the user
                org_id:
                  type: string
                  description: Optional organization ID for multi-tenant clients
              required:
                - user_id

      responses:
        '200':
          description: Successfully notified ScribeUp of updated Plaid accounts.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
        '400':
          description: Bad request.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    enum:
                      - Missing user_id
                      - Invalid Client
                      - Invalid user_id
                    example: Missing user_id
        '403':
          description: Forbidden.

  /api/v1/webhook/transactions/batch:
    post:
      tags:
        - Transactions Integration
      summary: Send transactions via webhook
      description: |-
        Push real-time or batched transaction events to ScribeUp for ingestion.

        **Important:** Each request must contain transactions for a single user only. All transactions in the `transactions` array must share the same `user_id`. To send transactions for multiple users, submit a separate request per user. Each request is limited to a maximum of 1000 transactions.

        Flow:
          - Your system collects/updates transactions
          - POST an array of transactions under `transactions`

      parameters:
        - in: header
          name: Authorization
          required: true
          schema:
            type: string
            example: Bearer {API_TOKEN}
          description: Bearer token for authorization
        - in: header
          name: X-Client-ID
          required: true
          schema:
            type: string
            example: "{CLIENT_ID}"
          description: Issued Client ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                org_id:
                  type: string
                  description: Optional organization ID for multi-tenant clients
                transactions:
                  type: array
                  description: Array of transaction objects
                  maxItems: 1000
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Unique identifier for the transaction
                      user_id:
                        type: string
                        description: Client's unique identifier for user
                      authorized_datetime:
                        type: string
                        format: date-time
                        description: Timestamp of authorization
                      datetime:
                        type: string
                        format: date-time
                        description: Timestamp when the transaction posted/settled
                      status:
                        type: string
                        description: Transaction status (e.g., `pending`, `closed`, `reversed`)
                      amount:
                        type: number
                        description: Decimal amount
                        example: 9.97
                      currency:
                        type: string
                        description: ISO currency code (e.g., `USD`)
                      description:
                        type: string
                        description: Raw transaction description string
                      country:
                        type: string
                        description: ISO alpha-2 country code (e.g., `US`)
                      account_id:
                        type: string
                        description: ID of the related account
                      account_metadata:
                        type: object
                        description: Nested account metadata object
                        properties:
                          account_name:
                            type: string
                            description: Display name of the account (e.g., "Personal Checking")
                          account_mask:
                            type: string
                            description: Masked account identifier (typically last 4 digits)
                          account_type:
                            type: string
                            description: High-level account type (e.g., `depository`)
                          account_subtype:
                            type: string
                            description: Subtype (e.g., `checking`)
                        required:
                          - account_name
                          - account_mask
                          - account_type
                          - account_subtype
                      type:
                        type: string
                        description: Transaction type (e.g., `online`, `pos`)
                      mcc:
                        type: integer
                        description: Merchant Category Code
                    required:
                      - id
                      - user_id
                      - authorized_datetime
                      - datetime
                      - status
                      - amount
                      - currency
                      - description
                      - country
                      - account_id
                      - account_metadata
              required:
                - transactions
            example:
              transactions:
                - id: "5cafaab8-535a-44ee-9bb3-03be4326fc39"
                  user_id: "user123"
                  authorized_datetime: "2024-07-24T19:13:02Z"
                  datetime: "2024-07-24T19:13:02Z"
                  status: "closed"
                  amount: 9.97
                  currency: "USD"
                  description: "APPLE.COM/BILL"
                  country: "US"
                  account_id: "2b8abcb5-3ce6-4882-b093-ce737b0dd3d9"
                  account_metadata:
                    account_name: "Personal Checking"
                    account_mask: "4567"
                    account_type: "depository"
                    account_subtype: "checking"
                  mcc: 1234
                  type: "online"
      responses:
        '200':
          description: Batch accepted and queued for processing
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    enum:
                      - Invalid Client
                      - Unsupported Client
                      - Invalid Payload
              examples:
                invalid_client:
                  value: { "message": "Invalid Client" }
                unsupported_client:
                  value: { "message": "Unsupported Client" }
                invalid_payload:
                  value: { "message": "Invalid Payload" }
        '401':
          description: Missing or invalid credentials
        '403':
          description: Forbidden

components:
  schemas:
    GetUsersResponse:
      description: Data regarding user status.
      type: array
      items:
        $ref: '#/components/schemas/GetUserResponse'

    GetUserResponse:
      description: Data regarding user status.
      type: object
      properties:
        id:
          description: ScribeUp ID (UUID).
          type: string
          format: uuid
          example: 920a4b16-95d9-4d45-bb1a-05e2e4aebce1
        external_id:
          description: Client unique ID.
          type: string
          example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c, jacob-doe-201284102, johndoe@gmail.com
        created:
          description: Timestamp when the user was added.
          type: string
          format: date-time
          example: '2024-04-21T17:30:30Z'
        status:
          description: User activation status.
          type: string
          enum: [inactive, active, terminated, deactivated]
        changelog:
          type: array
          description: Array of status changes for the user.
          items:
            type: object
            properties:
              action:
                type: string
                enum: [activate, deactivate, terminate]
                description: The action performed on the user.
              note:
                type: string
                description: Optional note about the action.
                nullable: true
              created:
                type: string
                format: date-time
                description: Timestamp when the action was performed.
                example: '2024-08-13T20:18:00.277758Z'

    GetRecurrencesResponse:
      description: List of user's recurring bills.
      type: array
      items:
        $ref: '#/components/schemas/RecurrenceResponse'

    RecurrenceResponse:
      description: Data regarding a recurring bill.
      type: object
      properties:
        id:
          description: Unique ID.
          type: string
          format: uuid
          example: 7788ce7e-383c-4b6f-93aa-295269add1e2
        merchant_id:
          description: Merchant ID.
          type: string
          format: uuid
          example: 5bb59fa1-3e92-496c-9ba2-333d9e28ed3c
        merchant_name:
          description: Merchant name.
          type: string
          example: Netflix
        frequency:
          description: Frequency of recurring bill.
          type: string
          enum: [weekly, bi-weekly, monthly, bi-monthly, quarterly, semi-yearly, yearly, other]
          example: monthly
        status:
          description: Status of recurring bill.
          type: string
          enum: [active, inactive, cancelled, external]
          example: active
        first_payment_date:
          description: Earliest charge date for the recurring bill.
          type: string
          format: date-time
          example: '2023-07-15T00:00:00Z'
        latest_payment_date:
          description: Most recent charge date for the recurring bill.
          type: string
          format: date-time
          example: '2024-05-15T00:00:00Z'
        next_expected_payment_date:
          description: Date of the next expected payment.
          type: string
          format: date-time
          example: '2024-06-15T00:00:00Z'
        latest_payment_amount_cents:
          description: Amount for the most recent charge.
          type: integer
          example: 1500
        total_spend_cents:
          description: Sum of all charges for the recurring merchant.
          type: integer
          example: 16500
        connected:
          description: Whether the recurring bill is on the client account.
          type: boolean
          example: true
        migrated:
          description: Whether the recurring bill was migrated onto the client account from an external account.
          type: boolean
          example: true

    GetConnectedAccountsResponse:
      description: List of user's connected accounts.
      type: array
      items:
        $ref: '#/components/schemas/ConnectedAccountResponse'

    ConnectedAccountResponse:
      description: Data regarding a connected account.
      type: object
      properties:
        institution_id:
          description: Institution ID.
          type: string
          format: uuid
          example: '69e8813e-8be9-45cb-a097-cc996ac45b5a'
        institution_name:
          description: Institution name.
          type: string
          example: Bank of America
        type:
          description: Account type.
          type: string
          enum: [credit, debit, savings]
          example: debit
        total_spend_cents:
          description: Sum of outgoing transactions.
          type: integer
          example: 3543000
        total_deposits_cents:
          description: Sum of incoming deposits.
          type: integer
          example: 4785000
        active_subscriptions:
          description: Number of active subscriptions identified on the account.
          type: integer
          example: 4
        active_loans:
          description: Number of loans identified on the account.
          type: integer
          example: 2

    GetLoansResponse:
      description: List of user's identified loans.
      type: array
      items:
        $ref: '#/components/schemas/LoanResponse'

    LoanResponse:
      description: Data regarding a loan.
      type: object
      properties:
        id:
          description: Unique ID.
          type: string
          format: uuid
          example: 76316819-db7e-4a0e-a781-13abc830dbce
        merchant_id:
          description: Merchant ID.
          type: string
          format: uuid
          example: 573d46ef-8446-42a6-a0e4-edf3454da484
        merchant_name:
          description: Merchant name.
          type: string
          example: DriveTime
        loan_type:
          description: Loan type.
          type: string
          enum: [auto, student, mortgage, credit_card, other]
          example: auto
        frequency:
          description: Frequency of loan payments.
          type: string
          enum: [weekly, bi-weekly, monthly, bi-monthly, quarterly, semi-yearly, yearly, other]
          example: monthly
        status:
          description: Status of the loan.
          type: string
          enum: [active, inactive, cancelled, external]
          example: active
        first_payment_date:
          description: Earliest charge date for the loan.
          type: string
          format: date-time
          example: '2023-07-15T00:00:00Z'
        latest_payment_date:
          description: Most recent charge date for the loan.
          type: string
          format: date-time
          example: '2024-05-15T00:00:00Z'
        next_expected_payment_date:
          description: Date of the next expected payment.
          type: string
          format: date-time
          example: '2024-06-15T00:00:00Z'
        latest_payment_amount_cents:
          description: Amount for the most recent charge.
          type: integer
          example: 15000
        total_spend_cents:
          description: Sum of all charges for the loan.
          type: integer
          example: 165000
        connected:
          description: Whether the loan is on the client account.
          type: boolean
          example: true
        migrated:
          description: Whether the loan was migrated onto the client account from an external account.
          type: boolean
          example: true

  examples:
    GetUsersResponseExample:
      summary: User data
      value:
        - id: 1c778a83-5a5d-4ca8-9673-6ac9d4adcf0d
          external_id: janedoe_inactive@gmail.com
          created: '2024-03-21T17:30:30Z'
          changelog: []
          status: inactive
        - id: 3dc56283-ca96-4936-9c0f-7b4d3de06578
          external_id: janedoe_activated@gmail.com
          created: '2024-03-21T17:30:30Z'
          changelog:
            - action: activate
              note: null
              created: '2024-04-21T17:32:30Z'
          status: active
        - id: 920a4b16-95d9-4d45-bb1a-05e2e4aebce1
          external_id: johndoe_deactivated@gmail.com
          created: '2024-04-21T17:30:30Z'
          changelog:
            - action: deactivate
              note: ClientAPI|
              created: '2024-08-13T20:18:00.277758Z'
            - action: activate
              note: null
              created: '2024-04-21T17:32:30Z'
          status: deactivated
        - id: 2a93d88b-6231-4f85-bdb4-19a568bf3fb8
          external_id: terminateduser@example.com
          created: '2024-02-20T17:30:30Z'
          changelog:
            - action: terminate
              note: 'User requested account termination.'
              created: '2024-08-10T15:30:00Z'
            - action: activate
              note: null
              created: '2024-02-21T12:00:00Z'
          status: terminated
    GetUserResponseExample:
      summary: User data
      value:
        id: 920a4b16-95d9-4d45-bb1a-05e2e4aebce1
        external_id: johndoe@gmail.com
        created: '2024-04-21T17:30:30Z'
        changelog:
          - action: deactivate
            note: ClientAPI|
            created: '2024-08-13T20:18:00.277758Z'
          - action: activate
            note: null
            created: '2024-04-21T17:32:30Z'
        status: deactivated
    GetRecurrencesResponseExample:
      summary: Recurring bill data
      value:
        - id: 7788ce7e-383c-4b6f-93aa-295269add1e2
          merchant_id: 5bb59fa1-3e92-496c-9ba2-333d9e28ed3c
          merchant_name: Netflix
          frequency: monthly
          status: active
          first_payment_date: '2023-07-15T00:00:00Z'
          latest_payment_date: '2024-05-15T00:00:00Z'
          next_expected_payment_date: '2024-06-15T00:00:00Z'
          latest_payment_amount_cents: 1500
          total_spend_cents: 16500
          connected: true
          migrated: true
        - id: de72b002-12fc-40ce-a15a-2b1c58dba52c
          merchant_id: 14b04b57-f0e9-4305-82e1-812d8f5c8ab3
          merchant_name: Geico
          frequency: semi-yearly
          status: inactive
          first_payment_date: '2022-03-15T00:00:00Z'
          latest_payment_date: '2023-09-15T00:00:00Z'
          next_expected_payment_date: null
          latest_payment_amount_cents: 65000
          total_spend_cents: 260000
          connected: false
          migrated: false
    GetConnectedAccountsResponseExample:
      summary: Connected account data
      value:
        - institution_id: '69e8813e-8be9-45cb-a097-cc996ac45b5a'
          institution_name: Bank of America
          type: debit
          total_spend_cents: 3543000
          total_deposits_cents: 4785000
          active_subscriptions: 1
          active_loans: 1
        - institution_id: '69e8813e-8be9-45cb-a097-cc996ac45b5a'
          institution_name: Bank of America
          type: credit
          total_spend_cents: 2821000
          total_deposits_cents: 0
          active_subscriptions: 6
          active_loans: 1
    GetLoansResponseExample:
      summary: Loan data
      value:
        - id: 76316819-db7e-4a0e-a781-13abc830dbce
          merchant_id: 573d46ef-8446-42a6-a0e4-edf3454da484
          merchant_name: DriveTime
          loan_type: auto
          frequency: monthly
          status: active
          first_payment_date: '2023-07-15T00:00:00Z'
          latest_payment_date: '2024-05-15T00:00:00Z'
          next_expected_payment_date: '2024-06-15T00:00:00Z'
          latest_payment_amount_cents: 15000
          total_spend_cents: 165000
          connected: true
          migrated: true
        - id: de72b002-12fc-40ce-a15a-2b1c58dba52c
          merchant_id: 14b04b57-f0e9-4305-82e1-812d8f5c8ab3
          merchant_name: Rocket Mortgage
          loan_type: mortgage
          frequency: monthly
          status: active
          first_payment_date: '2023-03-15T00:00:00Z'
          latest_payment_date: '2024-05-15T00:00:00Z'
          next_expected_payment_date: '2024-06-15T00:00:00Z'
          latest_payment_amount_cents: 360000
          total_spend_cents: 5040000
          connected: false
          migrated: false

tags:
  - name: Authentication
    x-displayName: Authentication
    description: The Subscription Manager UI can be embedded using a secure iframe or launched via the native mobile SDK. This section outlines both options and customization approaches for different placements.

  - name: UI Integration
    x-displayName: UI Integration
    description: |-
      You can integrate ScribeUp using either the mobile SDK or a web iframe. **We recommend the mobile SDK whenever possible**, as it offers deeper functionality and better integration with the user’s operating system (e.g., mobile-specific behaviors, smoother transitions).

      In both cases, you will implement:
      - **Dedicated Page**: A dedicated Subscription Manager page where users can access the full experience.
      - **Preview Widget**: A persistent preview widget on high-traffic surfaces (like the homepage) to increase visibility and engagement.

      This section outlines both integration options and how to implement pre-built or custom preview widgets to fit your app.

  - name: sdk
    x-displayName: "SDK Integration"
    description: |-
      ## React Native
      The ScribeUp SDK for React Native allows you to embed the Subscription Manager experience directly inside your mobile app.

      It works by launching a WebView-based modal that mirrors the same user experience as the iframe integration, but adapted for mobile environments.

      By integrating the SDK, you'll also gain access to mobile-native capabilities that unlock support for additional merchant integrations.
      ### Installation
      ```html
      npm install @scribeup/react-native-scribeup
      ```

      **NPM Package**: [https://www.npmjs.com/package/@scribeup/react-native-scribeup](https://www.npmjs.com/package/@scribeup/react-native-scribeup)

      **Repository**: [https://github.com/ScribeUp/react-native-scribeup](https://github.com/ScribeUp/react-native-scribeup)

      **Latest Version**: See [NPM package](https://www.npmjs.com/package/@scribeup/react-native-scribeup) for current version and implementation details

      ## iOS
      ### Installation
      The ScribeUp iOS SDK is available via Swift Package Manager. To add the SDK to your project, add the following repository URL as a package dependency:

      ```
      https://github.com/ScribeUp/scribeup-sdk-ios
      ```

      **Repository**: [https://github.com/ScribeUp/scribeup-sdk-ios](https://github.com/ScribeUp/scribeup-sdk-ios)

      **Latest Version**: See [GitHub](https://github.com/ScribeUp/scribeup-sdk-ios?tab=readme-ov-file#installation) for current version and implementation details

      ## Android

      ### Installation

      The SDK is published to Maven Central. In your app's `build.gradle`:

      ```gradle
      repositories {
          mavenCentral()
      }

      dependencies {
          implementation("io.scribeup:scribeupsdk:X.X.X")
      }
      ```

      **Repository**: [https://github.com/ScribeUp/scribeup-sdk-android](https://github.com/ScribeUp/scribeup-sdk-android)

      **Latest Version**: See [GitHub](https://github.com/ScribeUp/scribeup-sdk-android?tab=readme-ov-file#installation) for current version and implementation details

  - name: iframe
    x-displayName: "iFrame Integration"
    description: |-
      You can embed the Subscription Manager directly into your web or mobile experience using a simple `iframe`.

      This method is quick to implement and requires no additional SDK setup. It's ideal for web-based portals or mobile apps with WebView support.

      #### Example iframe embed:
      ```html
      <iframe src="https://staging.widget.scribeup.io/preview#{AUTH_TOKEN}" width="100%" height="700" style="border:none;"></iframe>
      ```

      Make sure to replace `{AUTH_TOKEN}` with the short-lived token returned from the `/users/init` API. Tokens expire after 60 minutes and should be generated server-side.

      #### Full-Page Examples
      <img src="fullpage_desktop.png" alt="Full-page (desktop)" height="300" style="margin-right:60px" />
      <img src="fullpage_mobile.png" alt="Full-page (mobile)" height="300"/>

      > We recommend rendering the iframe on a dedicated page (e.g., “Manage Subscriptions”) to give users full access to ScribeUp’s features.

  - name: widget
    x-displayName: "Preview Widget Integration"
    description: |-
      You can embed ScribeUp's pre-built preview widget modules directly into your homepage or other surfaces of your app to drive engagement and visibility. If you'd prefer to use your own design, you can connect to ScribeUp's Reporting API to power your own preview widget.

      ### Pre-Built Preivew Widget

      **ScribeUp's Pre-Built Modules** (via SDK or iframe):
      - **Calendar View** (`/calendar-preview`): Show recent and upcoming subscriptions and bills in a calendar layout.
      - **List View** (`/list-preview`): Display a scrollable list of upcoming charges.

      These modules can be delivered via iframe or SDK depending on your platform.

      **Preview Widget UI States**: The embedded experience includes built-in states:
      - Onboarding (first-time setup)
      - No upcoming charges
      - Active subscriptions and bills
      - Calendar display


      <img src="pre_built_widgets.png" alt="Pre-Built Preview Widgets" style="margin-right:60px" />


      ### Native Widget (Custom UI using APIs)
      If you prefer to build a native UI, you can use ScribeUp's [Reporting API](#tag/Reporting) to retrieve the same bill, renewal, and account insights and render them in your own components.

  - name: Transactions Integration
    x-displayName: Transactions Integration
    description: |-
      ScribeUp uses your internal transactions data to identify recurring subscriptions and bills. You can either connect your Transactions API, use batch file delivery (e.g., SFTP or reverse ETL), or use webhooks.

      #### Integration Options:
      - **API Connection** (recommended): Connect your existing Transactions API to ScribeUp
      - **Batch Sync**: Use daily/weekly reverse ETL pipelines or share files via SFTP
      - **Webhooks**: Push real-time or batched transaction events directly to ScribeUp

      It's recommended that you provide at least **24 months** of transaction history per user for best detection accuracy.

      Each transaction should include the following fields:
      - **Required:**
        - `id`: Unique identifier for the transaction
        - `user_id`: Client's unique identifier for user
        - `authorized_datetime`: Timestamp of authorization
        - `datetime`: Timestamp when the transaction posted/settled
        - `status`: Transaction status (e.g., `pending`, `closed`, `reversed`)
        - `amount`: Decimal amount
        - `currency`: ISO currency code
        - `description`: Raw transaction description string
        - `country`: ISO alpha-2 country code (e.g., `US`)
        - `account_id`: ID of the related account
        - `account_metadata`: Optional if not provided via separate API
          - `account_name`
          - `account_mask`
          - `account_type` (e.g., `depository`)
          - `account_subtype` (e.g., `checking`)

        **Optional:**
        - `type`: Transaction type (e.g., `online`, `pos`)
        - `mcc`: Merchant Category Code

      #### Example Transaction Payload:
      ```json
      {
        "id": "5cafaab8-535a-44ee-9bb3-03be4326fc39",
        "user_id": "user123",
        "authorized_datetime": "2024-07-24T19:13:02",
        "datetime": "2024-07-24T19:13:02",
        "status": "closed",
        "amount": 9.97,
        "currency": "USD",
        "description": "APPLE.COM/BILL",
        "country": "US",
        "account_id": "2b8abcb5-3ce6-4882-b093-ce737b0dd3d9",
        "account_metadata": {
          "account_name": "Personal Checking",
          "account_mask": "4567",
          "account_type": "depository",
          "account_subtype": "checking",
        }
        "mcc": 1234,
        "type": "online"
      }
      ```

  - name: Plaid Integration
    x-displayName: Plaid Integration
    description: |-
      ScribeUp integrates with your existing **Plaid** instance using **processor tokens**, allowing users to connect external accounts and enabling ScribeUp to scan those accounts for recurring subscriptions and bills.

      #### Why use your Plaid instance?
      Using your existing Plaid connection offers several advantages:
      - **No duplicate account linking**: Reuse accounts already linked in your app.
      - **Control the data**: You own and manage all external account and transaction data.
      - **System-wide benefits**: Linked accounts can also power funding sources, fraud checks, and other internal tools.

      If you'd prefer not to integrate with your own Plaid keys, **ScribeUp can provide Plaid access on your behalf** — no extra engineering required. We'll handle the entire Plaid flow and transaction ingestion.

      ### Integration Overview

      **Step 1: Client Hosts the Plaid Link Flow**

      - Embed and manage your own Plaid Link UX (either in-app or via deep link).
      - Generate processor tokens (`p-tokens`) using the Plaid processor ID `scribeup`.
      - See [Plaid's documentation](https://plaid.com/docs/api/processors/#processortokencreate) for guidance on processor token creation.

      **Step 2: Notify ScribeUp via Webhook**

      - After generating processor tokens, notify ScribeUp using this endpoint:
        ```
        POST /api/v1/webhook/external-accounts
        ```

      **Step 3: Expose API for ScribeUp to Fetch Tokens**

      - Create a secure endpoint ScribeUp can call to retrieve all processor tokens for a given user.

      **Sample Contract**
      ```
      GET /v1/users/{user_id}/processor_tokens
      ```

      **Example Code (python)**
      ```python
      # Exchange the public token from Plaid Link for an access token.
      exchange_request = ItemPublicTokenExchangeRequest(public_token=public_token)
      exchange_token_response = client.item_public_token_exchange(exchange_request)
      access_token = exchange_token_response['access_token']

      # Fetch all accounts for this access token
      accounts_request = AccountsGetRequest(access_token=access_token)
      accounts_response = client.accounts_get(accounts_request)
      accounts = accounts_response['accounts']

      # Create processor tokens for all accounts
      ptokens = []
      for account in accounts:
          create_request = ProcessorTokenCreateRequest(
              access_token=access_token,
              account_id=account['account_id'],
              processor='scribeup'
          )
          create_response = client.processor_token_create(create_request)
          ptokens.append(create_response['processor_token'])

      return Response({"data": {"plaid_processor_tokens": ptokens}}, status.HTTP_200_OK)
      ```

      **Expected Response**
      ```json
      {
        "data": {
          "plaid_processor_tokens": [
            "processor-sandbox-123abc",
            "processor-sandbox-456def"
          ]
        }
      }
      ```

      - Input: `user_id` (your internal identifier, also passed to ScribeUp during auth)
      - Output: All processor tokens for linked accounts
      - Store generated tokens or use saved access tokens to regenerate them on demand
      - ScribeUp will authenticate using pre-shared API keys or credentials

      **Redirect after Plaid Flow**
      - Once the user finishes Plaid linking, redirect them back to ScribeUp to continue onboarding.

      ### Optional: Notify on Account Updates
      If a user adds or removes accounts in your experience, you can trigger a refresh by notifying ScribeUp using the webhook above. **This process is detailed below.**

      ### Don’t have a Plaid integration?
      ScribeUp can provide and manage the full Plaid linking experience on your behalf. Users can securely link accounts directly inside the Subscription Manager flow with no client-side implementation required.


  - name: Reporting
    x-displayName: Reporting API
    description: |-
      The Reporting API allows you to retrieve real-time user-level data from Subscription Manager, including active subscriptions, bills, linked accounts, and recurrence insights.

      This is ideal if:
      - You want to build a **custom, native UI** instead of using ScribeUp’s SDK or iframe
      - You need to surface **subscription intelligence** elsewhere in your app
      - You want to integrate user-level data into **internal dashboards or analytics tools**

      ### Common Use Cases
      - Power a **native widget** with real-time upcoming charges
      - Show personalized savings insights or alerts in your feed
      - Visualize linked accounts and track bill payment history
      - Use recurrence data to inform internal credit risk or churn models

  - name: Notification Webhook
    x-displayName: Notification Webhook
    description: |-
      This section explains how to receive real-time notifications from ScribeUp via webhook.

      You can use this to power both:

      - **Activation alerts**: For users who have activated Subscription Manager
      - **Pre-activation alerts**: For users who haven't yet activated Subscription Manager but should still receive nudges

      The webhook is designed to be used with your **CRM**, **marketing automation platform**, or **notifications infrastructure** to deliver push, email, or SMS messages.

      ### 1. Webhook Setup
      ScribeUp sends a `POST` request to your configured webhook endpoint whenever a notification should be delivered to a user.

      - **Method**: `POST`
      - **Content-Type**: `application/json`
      - **Sample Payload**:

      ```json
      {
        "id": "19cde91e-86d8-465a-80e5-fca40fb6e9dc",
        "user_id": "123456",
        "email": "user@example.com",
        "user_status": "active",
        "type": "bill_reminder",
        "merchant_names": ["Netflix", "Hulu"],
        "merchant_ids": ["netflix_001", "hulu_002"],
        "channels": ["email", "push"],
        "email_subject": "Your subscriptions are renewing soon",
        "email_content": "Netflix, Hulu, and 2 other services are renewing this week.",
        "push_title": "2 renewals this week",
        "push_body": "Netflix and Hulu are renewing soon",
        "recurrences": [
          {
            "id": "ff04d458-c376-42d4-8cae-6ae5ccbf5dce",
            "type": "subscription",
            "status": "active",
            "merchant": {
              "id": "netflix_001",
              "url": "netflix.com",
              "name": "Netflix",
              "logo_url": "https://example.com/logos/netflix.png"
            },
            "frequency": "monthly",
            "loan_type": null,
            "account_id": "65e3ab74-4cc0-4240-babf-a6956d7d780c",
            "amount_cents": 1599,
            "last_payment_date": "2025-04-18T06:57:08Z",
            "next_payment_date": "2025-05-18T06:57:08Z",
            "total_amount_cents": 4797,
            "monthly_amount_cents": 1599
          },
          {
            "id": "a8006635-f5ce-4863-819b-fa787eaa6746",
            "type": "subscription",
            "status": "active",
            "merchant": {
              "id": "hulu_002",
              "url": "hulu.com",
              "name": "Hulu",
              "logo_url": "https://example.com/logos/hulu.png"
            },
            "frequency": "monthly",
            "loan_type": null,
            "account_id": "65e3ab74-4cc0-4240-babf-a6956d7d780c",
            "amount_cents": 799,
            "last_payment_date": "2025-04-20T06:57:08Z",
            "next_payment_date": "2025-05-20T06:57:08Z",
            "total_amount_cents": 2397,
            "monthly_amount_cents": 799
          }
        ]
      }
      ```
      <br />

      ### 2. Payload Fields
      | **Field** | **Type** | **Description** |
      | --- | --- | --- |
      | `id` | string (uuid) | Required. Unique identifier for notification |
      | `user_id` | string | Required. Client's unique identifier for the user |
      | `email` | string | Optional. Used for email delivery if needed |
      | `user_status` | string | `active` or `inactive` |
      | `type` | string | Type of alert. See Section 3 |
      | `merchant_names` | array of strings | List of merchant names |
      | `merchant_ids` | array of strings | List of merchant identifiers |
      | `channels` | array of strings | Delivery methods to trigger <br> e.g. `["email", "push"]` |
      | `email_subject` | string | Subject line to use for email |
      | `email_content` | string | Body content for email |
      | `push_title`  | string | Title text for push notification |
      | `push_body` | string | Body content for push notification |
      | `recurrences` | array of objects | Recurring charges included in this notification |

      <br />

      ### 3. Notification Types
      | **Type** | **Description** |
      | ---- | --- |
      | `bill_reminder` | Reminds users about upcoming subscription renewals |
      | `renewal (coming soon)` | Alerts users about a subscription renewal |
      | `low_balance (coming soon)` | Alerts users when account balance is lower than <br> expected future charges |

      <br />

      ### 4. Delivery
      - Client is responsible for determining how each channel (`email`, `push`, `sms`) is handled based on the `channels` field.
      - The `user_status` field allows for separate messaging logic between active and pre-activation users.

      **Click Behavior**:

      Notifications should deep link directly into Subscription Manager's section within your app or web platform when possible.
      - For push: use a deep link to Subscription Manager
      - For email: include a CTA with a deep link to Subscription Manager
      <br />
      <br />

      ### 5.Example Visuals
      <div style="display: flex; gap: 40px;">
        <div style="text-align: center;">
          <div style="font-size: 12px; color: black; font-weight: 600;">Push Notification</div>
          <img src="alert_push.png" alt="Push Notification" height="500" />
        </div>
        <div style="text-align: center;">
          <div style="font-size: 12px; color: black; font-weight: 600;">Email Alert</div>
          <img src="alert_email.png" alt="Email Alert" height="500" />
        </div>
      </div>
      <br />

      ### 6. Webhook Signature (HMAC-SHA256)
      Each webhook includes a signed HMAC-SHA256 signature to verify authenticity.

      **Headers**:
      - `X-Webhook-Timestamp`: UNIX timestamp (in seconds)
      - `X-Webhook-Signature`: HMAC-SHA256 signature in the format sha256=<signature>

      **Signature Format**:
      The signature is computed as:
      ```bash
      HMAC_SHA256(secret, "<timestamp>.<raw_body>")
      ```

      - The raw JSON body is sent without extra whitespace
        - ```json.dumps(..., separators=(",", ":"))```
      <br />

      ### 7. Signature Verification (Python Example)
      ```python
      import hmac, hashlib, time

      def verify_webhook(secret, raw_body, timestamp, received_sig, tolerance=300):
          # 1. Check timestamp is within ±5 mins
          if abs(time.time() - int(timestamp)) > tolerance:
              return False

          # 2. Compute expected signature
          message = f"{timestamp}.{raw_body}"
          expected_sig = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()

          # 3. Compare securely
          return hmac.compare_digest(received_sig, expected_sig)
      ```

  - name: Card Data Integration
    x-displayName: Card Data Integration
    description: |-
      ScribeUp supports secure, PCI-DSS–compliant integrations for handling card data. **Raw card numbers are never stored** on ScribeUp systems.
      Instead, ephemeral, end-to-end–encrypted payloads are utilized.

      * **Request authenticity** – Every webhook and API request is signed with HMAC-SHA256.

      ---

      ### 1. Card Discovery – `/cards`

      Before requesting sensitive details, ScribeUp needs a non-PCI list of cards the user can pick from.
      Implement a **`POST /cards`** webhook that returns metadata such as `card_id`, last-4 mask, brand, name, and type.
      Once the end-user selects a card in the UI, all downstream calls are keyed by `card_id`, *not* `user_id`.
      <details>
      <summary>Technical Details</summary>

      ```http
      POST /cards
      Content-Type: application/json
      Authorization: Bearer {API_TOKEN}

      {
        "user_id": "123456"
      }
      ```

      ```json
      HTTP/1.1 200 OK
      {
        "cards": [
          {
            "card_id": "card_01H2M0VW6G9V8AYTZG15D9RVWB",
            "mask": "4242",
            "brand": "visa",
            "type": "credit",
            "name": "Sapphire Reserve"
          },
          {
            "card_id": "card_01H2M0X2SS6H2YF4G86RK6JM1M",
            "mask": "1881",
            "brand": "mastercard",
            "type": "debit",
            "name": "Everyday Checking"
          }
        ]
      }
      ```
      </details>

      The selected `card_id` is later supplied when the user initiates a **payment-method switch**.

      ---

      ### 2. Card Details Retrieval – `/cards/detail`

      Once the user confirms the card to use, ScribeUp needs the full PAN, expiry, and CVC.

      1. <details><summary><b>Webhook</b> – ScribeUp sends a request containing  <code>card_id</code>, <code>user_id</code>, and a Base-64 PEM <code>public_key</code>.</summary>

          ```
          POST /cards/detail
          Content-Type: application/json
          Authorization: Bearer {API_TOKEN}

          {
            "card_id": "card_01H2M0VW6G9V8AYTZG15D9RVWB",
            "user_id": "123456",
            "public_key": "<public_key_b64>"
          }
          ```
        </details>

      2. <div style="margin-bottom: 1em;"> <b>Fetch Card</b> – Your backend retrieves the raw card from your vault or processor.</div>


      3. <details><summary><b>Encrypt</b> – Build an AES-GCM payload and wrap the AES key with the provided RSA public key (RSA-OAEP).</summary>

         ```
         import base64, json
         from Crypto.Random import get_random_bytes
         from Crypto.Cipher import AES, PKCS1_OAEP
         from Crypto.Hash import SHA512
         from Crypto.PublicKey import RSA

         def encrypt_payload(public_key_b64: str, card_data: dict) -> dict:
             payload = json.dumps(card_data).encode()
             rsa_key = RSA.import_key(base64.b64decode(public_key_b64))
             aes_key, nonce = get_random_bytes(32), get_random_bytes(12)

             encrypted_key = PKCS1_OAEP.new(rsa_key, hashAlgo=SHA512).encrypt(aes_key)
             cipher = AES.new(aes_key, AES.MODE_GCM, nonce=nonce)
             ciphertext, tag = cipher.encrypt_and_digest(payload)

             return {
                 "data": base64.b64encode(ciphertext).decode(),
                 "encryption_envelope": {
                     "encrypted_request_key": base64.b64encode(encrypted_key).decode(),
                     "request_nonce": base64.b64encode(nonce).decode(),
                     "request_tag": base64.b64encode(tag).decode()
                 }
             }
         ```
        </details>

      4. <details><summary><b>Respond</b> – Include the encrypted payload in your HTTP 200 response to the webhook.</summary>

          ```
          HTTP/1.1 200 OK
          {
            "data": "<base64-ciphertext>",
            "encryption_envelope": {
              "encrypted_request_key": "<base64>",
              "request_nonce": "<base64>",
              "request_tag": "<base64>"
            }
          }
          ```
          </details>

      5. **Cleanup** – ScribeUp decrypts, processes the job, and discards the private key; raw card data is never persisted.

      ---

      ### 3. Transport Security

      All API calls and webhooks include an **HMAC-SHA256** signature:

      *Headers*

      ```
      X-Webhook-Timestamp: <unix_epoch_seconds>
      X-Webhook-Signature: sha256=<signature>
      ```

      Compute the signature as:

      ```
      HMAC_SHA256(secret, "<timestamp>.<raw_body>")
      ```

x-tagGroups:
  - name: Authentication
    tags:
      - Authentication
  - name: UI Integration
    tags:
      - UI Integration
      - sdk
      - iframe
      - widget
  - name: Transactions Integration
    tags:
      - Transactions Integration
  - name: Card Data Integration
    tags:
      - Card Data Integration
  - name: Plaid Integration
    tags:
      - Plaid Integration
  - name: Notifications
    tags:
      - Notification Webhook
  - name: Reporting API
    tags:
      - Reporting
