openapi: 3.0.3
info:
  title: PaySentinel SaaS API
  description: |
    API for the PaySentinel SaaS, supporting WordPress plugin integration and dashboard functionality.

    ## Authentication

    Two authentication methods are supported:
    - **HMAC**: For WordPress plugin requests (X-PaySentinel-Signature)
    - **SessionAuth**: For dashboard web requests (Supabase session cookie)
  version: 1.0.0

servers:
  - url: https://paysentinel.caplaz.com/api
    description: Production server

tags:
  - name: License Management
    description: License validation and synchronization
  - name: Alerts
    description: Alert delivery
  - name: Integrations
    description: Third-party integrations management
  - name: Dashboard
    description: User dashboard operations
  - name: Billing
    description: Subscription and checkout

components:
  securitySchemes:
    HMAC:
      type: apiKey
      in: header
      name: X-PaySentinel-Signature
      description: |
        HMAC-SHA256 signature for WordPress plugin authentication.

        **Required headers (all must be present):**
        - `X-PaySentinel-License-Key`: The license key
        - `X-PaySentinel-Timestamp`: Unix timestamp in seconds (within 5 minutes of server time)
        - `X-PaySentinel-Signature`: HMAC-SHA256 hex digest of the message
        - `X-PaySentinel-Site-Url`: Site URL (optional, depends on endpoint)

        **Signature generation:**
        ```
        message = timestamp + "." + request_body
        signature = HMAC-SHA256(site_secret, message)
        ```

        **Note:** For empty request bodies, both `timestamp.` and `timestamp` formats are accepted for backward compatibility.

        **Security:** All HMAC credentials MUST be passed as HTTP headers. Query parameters are not supported.

        **Rate limiting:** Varies by endpoint (see individual operations)

    SessionAuth:
      type: apiKey
      in: cookie
      name: sb-access-token
      description: Supabase session cookie for authenticated dashboard users.

  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error message
        details:
          type: string
          description: Additional error details (optional)
      additionalProperties: true

    LicenseValidationRequest:
      type: object
      required:
        - license_key
        - site_url
      properties:
        license_key:
          type: string
          description: The license key to validate
          example: "XXXX-XXXX-XXXX-XXXX"
        site_url:
          type: string
          format: uri
          description: The WordPress site URL (must be valid HTTP/HTTPS URL)
          example: "https://example.com"

    LicenseValidationResponse:
      type: object
      required:
        - expiration_ts
        - plan
        - features
        - message
      properties:
        expiration_ts:
          type: string
          format: date-time
          description: License expiration timestamp
          example: "2025-12-31T23:59:59Z"
        plan:
          type: string
          description: Plan type (free, starter, professional, enterprise)
          example: "professional"
        features:
          type: object
          description: Feature flags for this plan
          properties:
            email_alerts:
              type: boolean
            slack_alerts:
              type: boolean
            max_sites:
              type: integer
        quota:
          type: object
          nullable: true
          description: Current quota usage
          properties: {}
        message:
          type: string
          description: Status message
          example: "License is valid."

    LicenseActivationResponse:
      type: object
      required:
        - site_registration
        - license_info
      properties:
        site_registration:
          type: object
          required:
            - registered
            - site_secret
          properties:
            registered:
              type: boolean
              description: Whether the site is registered
            site_secret:
              type: string
              description: 64-character HMAC secret for subsequent requests
        license_info:
          type: object
          properties:
            plan:
              type: string
              description: Plan type
            expires_at:
              type: string
              format: date-time
              description: License expiration timestamp
              nullable: true
        message:
          type: string
          example: "License is valid. Site is registered."

    SyncResponse:
      type: object
      required:
        - valid
        - plan
        - features
      properties:
        valid:
          type: boolean
          description: Whether the license is currently valid
        plan:
          type: string
          description: Current plan type
          example: "professional"
        features:
          type: object
          description: Feature flags for this plan
        quota:
          type: object
          nullable: true
          description: Current quota usage
        expires_at:
          type: string
          format: date-time
          description: License expiration date

    AlertRequest:
      type: object
      required:
        - license_key
        - message
      properties:
        license_key:
          type: string
          description: License key for quota verification
        message:
          type: string
          description: Alert message content
          example: "Payment failed for Order #12345"
        channels:
          type: array
          items:
            type: string
            enum: [SLACK, EMAIL, DISCORD, TEAMS]
          description: |
            Array of channels to send alert to. If empty or omitted, 
            triggers all enabled notification channels configured in the account.
          example: ["SLACK", "EMAIL"]
        site_url:
          type: string
          description: WordPress site URL for verification
        data:
          type: object
          additionalProperties: true
          description: Additional contextual data for the alert

    AlertResponse:
      type: object
      required:
        - success
        - message
      properties:
        success:
          type: boolean
        message:
          type: string
          example: "Alert delivered successfully"

    SlackStatusResponse:
      type: object
      required:
        - active
      properties:
        active:
          type: boolean
          description: Whether the integration is currently active
        workspace_name:
          type: string
          description: Slack workspace name
          example: "My Workspace"
        channel_name:
          type: string
          description: Connected channel name
          example: "Connected"
        connected_at:
          type: string
          format: date-time
          description: When the integration was created
        last_alert_sent:
          type: string
          format: date-time
          nullable: true
          description: Timestamp of last alert sent (if any)

    SlackTestRequest:
      type: object
      required:
        - integration_id
        - message
      properties:
        integration_id:
          type: string
          description: ID of the Slack integration to test
        message:
          type: string
          description: Test message to send
          example: "This is a test alert from PaySentinel"

    SlackDisconnectRequest:
      type: object
      required:
        - integration_id
      properties:
        integration_id:
          type: string
          description: ID of the Slack integration to disconnect

    SuccessResponse:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "Operation completed successfully"

    License:
      type: object
      properties:
        id:
          type: string
          description: License ID from PayBee
        key:
          type: string
          description: License key
        price_id:
          type: string
          description: Stripe price ID
        status:
          type: string
          description: License status (active, expired, etc)
        expires_at:
          type: string
          format: date-time
          description: Expiration date
        activated_devices:
          description: List of activated devices/sites
        plan_name:
          type: string
          description: Display name of the plan (Free, Starter, Pro, Agency)
          example: "Pro"
        plan_color:
          type: string
          description: Hex color code for the plan badge
          example: "#46b450"

    LicensesResponse:
      type: object
      required:
        - licenses
      properties:
        licenses:
          type: array
          items:
            $ref: "#/components/schemas/License"

    DeactivateSiteRequest:
      type: object
      required:
        - device_id
        - license_key
      properties:
        device_id:
          type: string
          description: Device/site ID to deactivate (typically the site URL)
        license_key:
          type: string
          description: License key that owns this device

    Integration:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          description: Integration name (e.g., workspace name)
        type:
          type: string
          description: Integration type
          example: "SLACK"
        site_url:
          type: string
          description: Associated WordPress site URL
        license_key:
          type: string
          description: Associated license key
        is_active:
          type: boolean
          description: Whether the integration is active
        created_at:
          type: string
          format: date-time

    IntegrationsResponse:
      type: object
      required:
        - integrations
      properties:
        integrations:
          type: array
          items:
            $ref: "#/components/schemas/Integration"

    AlertDelivery:
      type: object
      properties:
        id:
          type: string
          format: uuid
        license_key:
          type: string
        channel:
          type: string
          enum: [SLACK, EMAIL, DISCORD, TEAMS]
        status:
          type: string
          example: "delivered"
        recipient:
          type: string
          nullable: true
        message:
          type: string
        delivered_at:
          type: string
          format: date-time

    AlertDeliveriesResponse:
      type: object
      required:
        - deliveries
      properties:
        deliveries:
          type: array
          items:
            $ref: "#/components/schemas/AlertDelivery"

    CheckoutRequest:
      type: object
      required:
        - priceId
      properties:
        priceId:
          type: string
          description: Stripe price ID to purchase
          example: "price_1234567890"

    CheckoutResponse:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          description: Stripe checkout session URL
          example: "https://checkout.stripe.com/..."

    UpdateSubscriptionRequest:
      type: object
      required:
        - priceId
      properties:
        priceId:
          type: string
          description: New Stripe price ID to change subscription to
          example: "price_9876543210"

    UpdateSubscriptionResponse:
      type: object
      required:
        - message
        - subscriptionId
        - licenseId
        - status
      properties:
        message:
          type: string
          example: "Subscription updated"
        subscriptionId:
          type: string
          description: PayBee subscription ID
        licenseId:
          type: string
          description: Updated license ID
        status:
          type: string
          description: Subscription status
          example: "active"

    SubscribeRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
          description: Email address to subscribe to waitlist
          example: "user@example.com"

    SubscribeResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          example: "Subscribed successfully"

    SupportRequest:
      type: object
      required:
        - subject
        - message
        - email
      properties:
        subject:
          type: string
          description: Support request subject
          example: "Need help with license activation"
        message:
          type: string
          description: Support request message body
          example: "I'm having trouble activating my license on my WordPress site."
        name:
          type: string
          description: Sender's name (optional)
          example: "John Doe"
        email:
          type: string
          format: email
          description: Contact email address
          example: "john@example.com"

    ProInsightsResponse:
      type: object
      required:
        - gatewayStatuses
        - benchmarks
      properties:
        gatewayStatuses:
          type: array
          description: Status of all monitored payment gateways
          items:
            type: object
            properties:
              gateway:
                type: string
                example: "stripe"
              status:
                type: string
                enum: [operational, degraded, down]
              last_checked:
                type: string
                format: date-time
        benchmarks:
          type: object
          nullable: true
          description: Industry benchmark data (if enabled)
          properties:
            industry:
              type: string
              example: "ecommerce"
            data:
              type: array
              items:
                type: object

paths:
  /activate-license:
    post:
      tags:
        - License Management
      summary: Activate Site
      security: [] # No auth required - this is the bootstrap endpoint
      description: |
        Registers a site URL to a license key and returns an HMAC secret.
        This is a one-time operation.

        **Rate limit:** 20 requests per minute per IP address
      operationId: activateLicense
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LicenseValidationRequest"
      responses:
        "200":
          description: Site successfully activated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LicenseActivationResponse"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: License invalid or limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /validate-license:
    post:
      tags:
        - License Management
      summary: Validate License
      security:
        - HMAC: []
      description: |
        Validates an existing license activation. Requires HMAC authentication.

        **Rate limit:** 20 requests per minute per IP address
      operationId: validateLicense
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LicenseValidationRequest"
      responses:
        "200":
          description: License is valid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LicenseValidationResponse"
        "401":
          description: Unauthorized (Invalid HMAC signature)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: License is invalid or expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "Invalid license"
                details: "Devices registered: 3"
        "429":
          description: Rate limit exceeded
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds until rate limit resets
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "Rate limit exceeded. Maximum 20 validations per minute."
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /sync:
    get:
      tags:
        - License Management
      summary: Sync License & Quotas
      description: |
        Fetches current plan, features, and quota limits for a registered site.

        **Authentication:** Requires HMAC authentication via headers (see HMAC security scheme for details).

        **Required headers:**
        - `X-PaySentinel-License-Key`: The license key
        - `X-PaySentinel-Timestamp`: Unix timestamp (within 5 minutes)
        - `X-PaySentinel-Signature`: HMAC-SHA256 signature
        - `X-PaySentinel-Site-Url`: Site URL (optional)
      operationId: syncLicense
      security:
        - HMAC: []
      responses:
        "200":
          description: Sync data retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SyncResponse"
        "401":
          description: Unauthorized (invalid HMAC signature)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Invalid or expired license
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /alerts:
    post:
      tags:
        - Alerts
      summary: Send Alert
      description: |
        Triggers alerts on configured notification channels from the WP Plugin.

        **Rate limit:** Varies based on plan and quota

        **Behavior:**
        - If `channels` array is provided and non-empty, sends only to those specified channels
        - If `channels` is empty or omitted, sends to all enabled notification channels configured in the account
        - Supported channels: SLACK, EMAIL, DISCORD, TEAMS
      operationId: sendAlert
      security:
        - HMAC: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AlertRequest"
      responses:
        "200":
          description: Alert delivered successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AlertResponse"
        "400":
          description: Bad request (missing required fields)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (invalid HMAC signature)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Quota exceeded or invalid license
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Integration service error (Slack/Email provider failure)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    get:
      tags:
        - Dashboard
      summary: Get Alert History
      description: |
        Retrieves the history of alerts sent for the user's licenses.
      operationId: getAlertHistory
      security:
        - SessionAuth: []
      responses:
        "200":
          description: Alert history retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AlertDeliveriesResponse"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /integrations/slack/status:
    get:
      tags:
        - Integrations
      summary: Check Slack Integration Status
      description: |
        Verifies if a Slack integration is still valid and active.

        Performs an active check against Slack's API to validate the OAuth token.
      operationId: checkSlackStatus
      security:
        - HMAC: []
      parameters:
        - name: integration_id
          in: query
          required: true
          schema:
            type: string
          description: ID of the Slack integration to check
      responses:
        "200":
          description: Status retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SlackStatusResponse"
        "400":
          description: Missing integration_id parameter
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (invalid HMAC signature)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Unauthorized access to this integration
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Integration not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "Integration not found"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /integrations/slack/test:
    post:
      tags:
        - Integrations
      summary: Send Test Slack Alert
      description: Sends a test message to verify Slack integration is working
      operationId: testSlackIntegration
      security:
        - HMAC: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SlackTestRequest"
      responses:
        "200":
          description: Test alert sent successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "400":
          description: Missing required fields
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (invalid HMAC signature)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Unauthorized access to this integration
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Integration not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error or integration secret missing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Slack API error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /integrations/slack:
    delete:
      tags:
        - Integrations
      summary: Disconnect Slack Integration
      description: Removes a Slack integration and deletes its secure tokens from vault
      operationId: disconnectSlack
      security:
        - HMAC: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SlackDisconnectRequest"
      responses:
        "200":
          description: Integration removed successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "400":
          description: Missing integration_id
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (invalid HMAC signature)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Unauthorized access to this integration
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Integration not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /licenses:
    get:
      tags:
        - Dashboard
      summary: List User Licenses
      description: |
        Retrieves all licenses associated with the authenticated user's email.

        **Authentication:** Requires valid Supabase session cookie
      operationId: listLicenses
      security:
        - SessionAuth: []
      responses:
        "200":
          description: Licenses retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LicensesResponse"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /sites:
    delete:
      tags:
        - Dashboard
      summary: Deactivate Site
      description: |
        Deactivates a specific site (device) from a license via PayBee API.

        **Note:** The device_id is typically the WordPress site URL.
      operationId: deactivateSite
      security:
        - SessionAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeactivateSiteRequest"
      responses:
        "200":
          description: Site deactivated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "400":
          description: Missing required fields
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Device not found (doesn't exist in PayBee)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error or PayBee API error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /integrations:
    get:
      tags:
        - Dashboard
      summary: List User Integrations
      description: |
        Returns all integrations (Slack, etc.) for the authenticated user.

        **Authentication:** Requires valid Supabase session cookie
      operationId: listIntegrations
      security:
        - SessionAuth: []
      responses:
        "200":
          description: Integrations retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IntegrationsResponse"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /integrations/{id}:
    delete:
      tags:
        - Dashboard
      summary: Delete Integration
      description: Deletes a specific integration owned by the authenticated user
      operationId: deleteIntegration
      security:
        - SessionAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Integration ID to delete
      responses:
        "200":
          description: Integration deleted successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Integration not found or not owned by user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /checkout:
    post:
      tags:
        - Billing
      summary: Create Stripe Checkout Session
      description: |
        Initiates a Stripe checkout session for a subscription plan.

        **Authentication:** Requires valid Supabase session (uses authenticated user's email)
      operationId: createCheckout
      security:
        - SessionAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CheckoutRequest"
      responses:
        "200":
          description: Checkout session created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutResponse"
        "400":
          description: Missing priceId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: User already has an active subscription
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "User already has an active subscription"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /update-subscription:
    post:
      tags:
        - Billing
      summary: Update Subscription Plan
      description: |
        Updates the authenticated user's subscription to a new plan/price.

        **Authentication:** Requires valid Supabase session (user must be logged in)
      operationId: updateSubscription
      security:
        - SessionAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateSubscriptionRequest"
      responses:
        "200":
          description: Subscription updated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UpdateSubscriptionResponse"
        "400":
          description: Missing required field (priceId)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error or PayBee API error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /subscribe:
    post:
      tags:
        - Dashboard
      summary: Subscribe to Waitlist
      description: |
        Subscribes an email to the PaySentinel waitlist/newsletter.

        **Authentication:** None required (public endpoint)
      operationId: subscribeEmail
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscribeRequest"
      responses:
        "200":
          description: Subscription successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscribeResponse"
        "400":
          description: Invalid email address
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /support:
    post:
      tags:
        - Dashboard
      summary: Submit Support Request
      description: |
        Submits a support request. Can be used by both authenticated and unauthenticated users.

        If the user is authenticated, their plan information is included for priority handling.

        **Authentication:** Optional (public endpoint, but provides enhanced features for logged-in users)
      operationId: submitSupport
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SupportRequest"
      responses:
        "200":
          description: Support request submitted successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "400":
          description: Missing required fields
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /pro/insights:
    get:
      tags:
        - Dashboard
      summary: Get PRO Analytics Insights
      description: |
        Retrieves advanced analytics data for PRO and AGENCY plan users.

        **Features:**
        - Gateway status monitoring (proactive alerts)
        - Industry benchmarks (if enabled)

        **Authentication:** Requires valid Supabase session with PRO or AGENCY plan
      operationId: getProInsights
      security:
        - SessionAuth: []
      responses:
        "200":
          description: Insights data retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProInsightsResponse"
        "401":
          description: Unauthorized (no valid session)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Feature restricted to PRO users
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "Feature restricted to PRO users"
                required_plan: "professional"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/integrations/slack/connect:
    get:
      tags:
        - Integrations
      summary: Initiate Slack OAuth (WordPress Plugin)
      description: |
        Initiates the Slack OAuth flow from the WordPress plugin. This is the entry point for connecting Slack workspaces.

        **Authentication:** Requires HMAC authentication via headers or query parameters.

        **Flow:**
        1. WordPress plugin calls this endpoint with HMAC credentials
        2. Endpoint validates HMAC and redirects user to Slack authorization
        3. User authorizes the app on Slack
        4. Slack redirects to `/api/auth/slack/callback`
        5. Callback exchanges code for token and redirects back to WordPress with integration_id

        **HMAC Authentication (choose one method):**

        *Via Headers:*
        - `X-PaySentinel-License-Key`: The license key
        - `X-PaySentinel-Timestamp`: Unix timestamp (within 5 minutes)
        - `X-PaySentinel-Signature`: HMAC-SHA256 signature

        *Via Query Parameters:*
        - `license_key`: License key
        - `timestamp`: Unix timestamp (within 5 minutes)
        - `signature`: HMAC-SHA256 signature

        **Required query parameters:**
        - `license_key`: License key (also in header)
        - `site_url`: WordPress site URL
        - `return_url`: Where to redirect after OAuth completes
        - `state`: CSRF nonce from WordPress plugin
      operationId: initiateSlackOAuth
      security:
        - HMAC: []
      parameters:
        - name: license_key
          in: query
          required: true
          schema:
            type: string
          description: License key
        - name: timestamp
          in: query
          required: true
          schema:
            type: integer
          description: Unix timestamp (within 5 minutes)
        - name: signature
          in: query
          required: true
          schema:
            type: string
          description: HMAC-SHA256 signature
        - name: site_url
          in: query
          required: true
          schema:
            type: string
          description: WordPress site URL
        - name: return_url
          in: query
          required: true
          schema:
            type: string
          description: WordPress admin URL to return to after OAuth
        - name: state
          in: query
          required: false
          schema:
            type: string
          description: CSRF nonce from WordPress
      responses:
        "200":
          description: OAuth flow initiated successfully (redirects to Slack)
        "302":
          description: Redirects to Slack OAuth authorization page
        "401":
          description: Invalid HMAC credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /auth/slack/callback:
    get:
      tags:
        - Integrations
      summary: Slack OAuth Callback (WordPress Plugin)
      description: |
        OAuth callback handler for Slack integration initiated from WordPress plugin.

        This endpoint is called by Slack after user authorizes the app.
        It exchanges the authorization code for an access token and creates
        the integration record in the database.

        **Flow:**
        1. WordPress plugin initiates OAuth via `/api/integrations/slack/connect`
        2. User authorizes on Slack
        3. Slack redirects to this endpoint with `code` and `state`
        4. Backend exchanges code for access token
        5. Resolves user_id by looking up license_key
        6. Creates/updates integration record with token stored in vault
        7. Redirects user back to WordPress admin page

        **Note:** This endpoint only supports WordPress plugin flows (no dashboard flows).
        The return_url must be present in the OAuth state metadata.
      operationId: slackOAuthCallback
      security: []
      parameters:
        - name: code
          in: query
          required: true
          schema:
            type: string
          description: OAuth authorization code from Slack
        - name: state
          in: query
          required: true
          schema:
            type: string
          description: State parameter for CSRF protection (matches cookie)
        - name: error
          in: query
          schema:
            type: string
          description: Error code if OAuth failed
      responses:
        "200":
          description: OAuth callback processed successfully (redirects to WordPress)
        "302":
          description: OAuth callback processed, redirects back to WordPress admin
        "400":
          description: Invalid request (missing code/state/return_url)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: License not found or not associated with a user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
