openapi: 3.1.0
info:
  title: QuantaRoute Checkout Admin API
  version: 1.6.0
  description: |
    Merchant Admin REST API for checkout analytics, configuration, customer import, and AI tool integration.
    Authenticate with `x-api-key` (merchant `api_key` from developers.quantaroute.com).

    ## Webhook event types

    QuantaRoute delivers signed HTTP POST webhooks to the merchant's `webhook_url`. Events vary by payment mode:

    **Mode A — external PG (no `enablePayment`):**
    - `session.address_completed` — verified address ready; buyer about to leave widget
    - `session.phone_verified` — phone OTP succeeded
    - `session.email_verified` — email OTP succeeded
    - `session.completed` is **not** sent — mark orders paid after your PG confirms payment

    **Mode B — BYO Razorpay (`enablePayment` + merchant keys):**
    - `session.completed` — prepaid or COD checkout finished (primary order event)
    - `session.phone_verified`, `session.email_verified`, `session.address_completed` — identity and address milestones

    Configure webhooks via `PATCH checkout-admin?action=webhooks`. Test delivery via `POST checkout-admin?action=test_webhook`.
    Inspect delivery logs via `GET checkout-admin?action=webhook_deliveries`.

servers:
  - url: https://{project_ref}.supabase.co/functions/v1
    variables:
      project_ref:
        default: your-project-ref

security:
  - ApiKeyAuth: []

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

  schemas:
    ImportCustomerRow:
      type: object
      description: One customer address row for bulk import. DigiPin is verified lazily at checkout.
      properties:
        legacy_customer_id:
          type: string
          description: External customer ID from your OMS or Shopify export
        legacy_address_id:
          type: string
          description: External address ID for error correlation
        email:
          type: string
          format: email
        phone_e164:
          type: string
          description: E.164 phone, e.g. +919876543210
        flat_number:
          type: string
        building_name:
          type: string
        street_name:
          type: string
        locality:
          type: string
        district:
          type: string
        state:
          type: string
        pincode:
          type: string
          pattern: '^[0-9]{6}$'
        label:
          type: string
          description: Address label, e.g. Home or Office
        is_default:
          type: boolean

paths:
  /checkout-admin:
    get:
      summary: Query checkout admin data
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum:
              - funnel
              - sessions
              - webhook_deliveries
              - cod_config
              - webhooks
              - get_payment_config
              - import_status
        - name: from
          in: query
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: batch_id
          in: query
          description: Required when `action=import_status`
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: |
            Success. Response shape varies by `action`.

            **`get_payment_config`** — Returns whether Razorpay credentials are configured for the merchant.

            **`import_status`** — Returns batch progress and up to 100 row-level errors.
          content:
            application/json:
              schema:
                oneOf:
                  - description: Response for `action = get_payment_config`
                    type: object
                    required: [success, data]
                    properties:
                      success:
                        type: boolean
                        enum: [true]
                      data:
                        type: object
                        required: [razorpay_key_id_set]
                        properties:
                          razorpay_key_id_set:
                            type: boolean
                            description: True when a Razorpay key ID has been configured for the merchant
                  - description: Response for `action = import_status`
                    type: object
                    required: [success, data]
                    properties:
                      success:
                        type: boolean
                        enum: [true]
                      data:
                        type: object
                        required: [batch, errors, errors_truncated]
                        properties:
                          batch:
                            type: object
                            properties:
                              id:
                                type: string
                                format: uuid
                              status:
                                type: string
                                enum: [processing, completed, failed]
                              total_rows:
                                type: integer
                              imported_count:
                                type: integer
                              error_count:
                                type: integer
                              source:
                                type: string
                              created_at:
                                type: string
                                format: date-time
                              completed_at:
                                type: string
                                format: date-time
                                nullable: true
                          errors:
                            type: array
                            items:
                              type: object
                              properties:
                                row_index:
                                  type: integer
                                legacy_external_id:
                                  type: string
                                  nullable: true
                                error_code:
                                  type: string
                                error_message:
                                  type: string
                                created_at:
                                  type: string
                                  format: date-time
                          errors_truncated:
                            type: boolean
                            description: True when more than 100 errors exist
                  - description: Response for all other actions
                    type: object
        '404':
          description: Import batch not found (import_status)
    patch:
      summary: Update merchant checkout config
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [cod_config, webhooks, set_payment_config]
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - description: Body for `action = set_payment_config`
                  type: object
                  required: [razorpay_key_id, razorpay_secret_key]
                  properties:
                    razorpay_key_id:
                      type: string
                      description: Razorpay publishable key ID (e.g. rzp_live_…)
                    razorpay_secret_key:
                      type: string
                      description: Razorpay secret key — stored encrypted, never returned
                - description: Body for `action = webhooks`
                  type: object
                  properties:
                    webhook_url:
                      type: string
                      format: uri
                      description: HTTPS endpoint for checkout lifecycle events
                    webhook_secret:
                      type: string
                      description: HMAC secret for X-QuantaRoute-Signature verification
                - description: Body for `action = cod_config`
                  type: object
                  properties:
                    enable_cod:
                      type: boolean
                    cod_max_cart_value_paise:
                      type: integer
                    cod_blocked_pincodes:
                      type: array
                      items:
                        type: string
      responses:
        '200':
          description: |
            Updated. Response shape varies by `action`.

            **`set_payment_config`** — Confirms Razorpay credentials were saved.
          content:
            application/json:
              schema:
                oneOf:
                  - description: Response for `action = set_payment_config`
                    type: object
                    properties:
                      success:
                        type: boolean
                        enum: [true]
                  - description: Response for all other actions
                    type: object
    post:
      summary: Admin actions — test webhook or bulk customer import
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [test_webhook, import_customers]
      requestBody:
        description: Required for `import_customers`. Omit for `test_webhook`.
        content:
          application/json:
            schema:
              oneOf:
                - description: Body for `action = import_customers`
                  type: object
                  required: [rows]
                  properties:
                    batch_label:
                      type: string
                      description: Optional label for tracking this import batch
                    rows:
                      type: array
                      minItems: 1
                      maxItems: 5000
                      items:
                        $ref: '#/components/schemas/ImportCustomerRow'
      responses:
        '200':
          description: |
            Success. Response shape varies by `action`.

            **`test_webhook`** — Queues a test delivery (`session.phone_verified` payload).

            **`import_customers`** — Returns batch summary and per-row results.
          content:
            application/json:
              schema:
                oneOf:
                  - description: Response for `action = test_webhook`
                    type: object
                    properties:
                      success:
                        type: boolean
                        enum: [true]
                      message:
                        type: string
                  - description: Response for `action = import_customers`
                    type: object
                    required: [success, data]
                    properties:
                      success:
                        type: boolean
                        enum: [true]
                      data:
                        type: object
                        required: [batch_id, total_rows, imported_count, error_count, results]
                        properties:
                          batch_id:
                            type: string
                            format: uuid
                          total_rows:
                            type: integer
                          imported_count:
                            type: integer
                          error_count:
                            type: integer
                          results:
                            type: array
                            items:
                              type: object
                              properties:
                                row_index:
                                  type: integer
                                success:
                                  type: boolean
                                buyer_id:
                                  type: string
                                  format: uuid
                                address_id:
                                  type: string
                                  format: uuid
                                error:
                                  type: string
        '400':
          description: Invalid JSON, empty rows, or batch exceeds 5000 rows

  /checkout-email-otp:
    post:
      summary: Send or verify email OTP (buyer-facing, origin-guarded)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  required: [action, email, merchant_id]
                  properties:
                    action:
                      type: string
                      enum: [send]
                    email:
                      type: string
                      format: email
                    merchant_id:
                      type: string
                      format: uuid
                - type: object
                  required: [action, email, code, merchant_id]
                  properties:
                    action:
                      type: string
                      enum: [verify]
                    email:
                      type: string
                      format: email
                    code:
                      type: string
                      minLength: 1
                    merchant_id:
                      type: string
                      format: uuid
      responses:
        '200':
          description: OTP sent or session created. Send response includes `reused` when an existing code was reused (no Resend email).
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    properties:
                      success:
                        type: boolean
                      reused:
                        type: boolean
                        description: True when the stored code was reused (no new email sent)
                      message:
                        type: string
                  - type: object
                    properties:
                      success:
                        type: boolean
                      session_token:
                        type: string
                      session_id:
                        type: string
                      expires_at:
                        type: string
                      buyer_id:
                        type: string
        '401':
          description: Invalid or expired code
        '429':
          description: Rate limited (send or verify lockout)

  /checkout-funnel-events:
    post:
      summary: Log a checkout funnel step event
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [merchant_id, event_type]
              properties:
                merchant_id:
                  type: string
                  format: uuid
                event_type:
                  type: string
                step:
                  type: string
                device_type:
                  type: string
                  enum: [mobile, tablet, desktop]
                platform:
                  type: string
                  enum: [web, ios, android]
      responses:
        '200':
          description: Logged

  /checkout-payment:
    post:
      summary: Razorpay order create / verify / COD record
      description: |
        Handles Razorpay payment lifecycle actions for buyer checkout sessions.

        **Widget note:** The frontend checkout widget accepts an `orderConfirmationUrl` prop.
        After a successful payment, the browser redirects to this URL. This is a widget-level
        concern and is not sent as an API parameter to this endpoint.

        **Actions:**
        - `create_order` — create a Razorpay order for the session (requires `amount_paise`)
        - `verify` — verify Razorpay payment signature after buyer completes checkout
        - `record_cod` — record a Cash on Delivery order and fire `session.completed` webhook
      parameters:
        - name: x-session-token
          in: header
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [action]
              properties:
                action:
                  type: string
                  enum: [create_order, verify, record_cod]
                amount_paise:
                  type: integer
                  description: Required for `create_order`
                razorpay_order_id:
                  type: string
                  description: Required for `verify`
                razorpay_payment_id:
                  type: string
                  description: Required for `verify`
                razorpay_signature:
                  type: string
                  description: Required for `verify`
      responses:
        '200':
          description: Success. Response shape varies by `action`.
          content:
            application/json:
              schema:
                oneOf:
                  - description: Response for `action = verify`
                    type: object
                    properties:
                      success:
                        type: boolean
                      payment_method:
                        type: string
                      razorpay_order_id:
                        type: string
                        description: Razorpay order ID confirmed after payment verification
                      razorpay_payment_id:
                        type: string
                        description: Razorpay payment ID returned after successful verification
                  - description: Response for `action = create_order`
                    type: object
                    properties:
                      success:
                        type: boolean
                      razorpay_order_id:
                        type: string
                      razorpay_key_id:
                        type: string
                  - description: Response for `action = record_cod`
                    type: object
                    properties:
                      success:
                        type: boolean
                      payment_method:
                        type: string
                        enum: [cod]

  /checkout-offers:
    get:
      summary: UTM-matched offer banner for session
      parameters:
        - name: x-session-token
          in: header
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Offer or null
    post:
      summary: Admin — list or create UTM offers
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [list, create]
      responses:
        '200':
          description: Success

  /checkout-addresses:
    post:
      summary: Buyer saved-address actions and merchant serviceability check
      parameters:
        - name: x-session-token
          in: header
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  required: [action, pincode]
                  properties:
                    action:
                      type: string
                      enum: [serviceability_check]
                    pincode:
                      type: string
                      pattern: '^[0-9]{6}$'
                    lat:
                      type: number
                    lng:
                      type: number
                - type: object
                  required: [action]
                  properties:
                    action:
                      type: string
                      enum:
                        - list
                        - create
                        - update
                        - delete
                        - set_default
                        - address_completed
                        - cod_eligibility
                        - abandon
      responses:
        '200':
          description: Success. `serviceability_check` returns merchant courier coverage and fail-open metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      enabled:
                        type: boolean
                      checked:
                        type: boolean
                      serviceable:
                        type:
                          - boolean
                          - 'null'
                      pincode:
                        type: string
                      message:
                        type: string
                      block_checkout:
                        type: boolean
                      show_india_post_delivery_warning:
                        type: boolean
                      reason:
                        type: string
                        enum: [not_configured, invalid_pincode, merchant_error, merchant_unreachable, invalid_response]
        '401':
          description: Missing or invalid checkout session token

  /v1/pincode/validate/{pincode}:
    get:
      summary: Validate Indian pincode (QuantaRoute Geocoding API)
      servers:
        - url: https://api.quantaroute.com
      security:
        - ApiKeyAuth: []
      parameters:
        - name: pincode
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Validation result
