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

# Update Payment Request

> Update the status of a payment request. Any status transition is permitted.

<Info>
  This endpoint requires a **PRO plan or above**. If your plan does not include the External Billing API, the request returns `403` with error code `external_billing_api`.
</Info>

## What happens when you mark a request as `PAID`

Sending `"status": "PAID"` triggers the internal payment service in addition to updating the status field:

1. **`paid_at`** is recorded with the current timestamp.
2. **`external_payment_id`** is stored if provided (useful as a reference to your payment processor's transaction ID).
3. The platform checks whether the linked subscription has any remaining **unpaid cycle requests** (`PENDING` or `OVERDUE`).
   * If none remain → subscription status returns to **`active`** and any paused campaigns are **automatically resumed**.
   * If others remain → subscription stays in its current state until all are resolved.

## `external_payment_id` is optional

You can mark a request as `PAID` without providing `external_payment_id`. It is recommended when you have a transaction reference from your payment processor (e.g. `txn_abc123`, `pi_abc123` from Stripe), but the field is not required.

## Status reference

| Status     | When to use                                                                                 |
| ---------- | ------------------------------------------------------------------------------------------- |
| `PAID`     | Client has paid. Triggers subscription recovery if no other cycle requests are outstanding. |
| `PENDING`  | Reset to pending (e.g. if you marked paid by mistake).                                      |
| `FAILED`   | Payment attempt failed - client should be contacted.                                        |
| `CANCELED` | Charge is void - no payment needed.                                                         |
| `OVERDUE`  | Mark manually overdue (normally set automatically by the platform's sweep).                 |

<Warning>
  Setting a cycle request to `PAID` can change the subscription status and resume paused campaigns. Verify you are acting on the correct payment request before proceeding.
</Warning>


## OpenAPI

````yaml PATCH /api/v2/external-billing/payment-requests/{payment_request_id}/update
openapi: 3.1.0
info:
  title: VoiceAIWrapper API
  description: >-
    REST API for managing campaigns, leads, clients, web widgets, and voice
    providers on VoiceAIWrapper.
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://api.voiceaiwrapper.app
security:
  - bearerAuth: []
paths:
  /api/v2/external-billing/payment-requests/{payment_request_id}/update:
    patch:
      tags:
        - External Billing - Payment Requests
      summary: Update Payment Request
      description: >-
        Updates the status of a payment request. Any status transition is
        permitted.


        When setting `status` to `PAID`, the platform also runs the internal
        `mark_payment_request_paid` service, which:

        - Records the `paid_at` timestamp.

        - Stores the optional `external_payment_id` as a reference.

        - Checks whether the linked subscription has any remaining unpaid cycle
        requests; if not, it restores the subscription to **Active** and resumes
        paused campaigns.


        ## Authentication

        Requires `Authorization: Bearer <api_key>`. The API key must belong to a
        tenant on a **PRO plan or above**.
      operationId: updatePaymentRequest
      parameters:
        - name: payment_request_id
          in: path
          required: true
          description: Integer ID of the payment request to update.
          schema:
            type: string
            example: '10'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePaymentRequestBody'
            examples:
              mark_paid:
                summary: Mark as paid with transaction reference
                value:
                  status: PAID
                  external_payment_id: txn_abc123
              mark_paid_no_ref:
                summary: Mark as paid without reference
                value:
                  status: PAID
              mark_failed:
                summary: Mark as failed
                value:
                  status: FAILED
              mark_canceled:
                summary: Mark as canceled
                value:
                  status: CANCELED
      responses:
        '200':
          description: >-
            Payment request updated successfully. Returns a confirmation message
            and the full updated payment request object.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: Confirmation that the payment request status was updated.
                    example: Payment status updated successfully.
                  data:
                    $ref: '#/components/schemas/PaymentRequest'
              example:
                message: Payment status updated successfully.
                data:
                  id: '10'
                  client_id: '42'
                  client_name: Acme Corp
                  subscription_id: '1'
                  status: PAID
                  type: SUBSCRIPTION
                  amount: '99.00'
                  currency: USD
                  due_date: '2025-01-15T00:00:00'
                  grace_period_ends_at: '2025-01-22T00:00:00'
                  paid_at: '2025-01-14T09:30:00'
                  external_payment_id: txn_abc123
                  notes: null
                  line_items: []
                  period_start: '2025-01-01T00:00:00'
                  period_end: '2025-02-01T00:00:00'
                  created_at: '2025-01-01T00:00:00'
                  updated_at: '2025-01-14T09:30:00'
        '400':
          description: >-
            Invalid request body - missing `status`, unrecognised status value,
            or body is not a JSON object.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
              example:
                message: Invalid status 'UNKNOWN'.
                valid_values:
                  - PENDING
                  - PAID
                  - FAILED
                  - CANCELED
                  - OVERDUE
        '401':
          description: >-
            Authentication failed - `Authorization` header missing, malformed,
            or API key not found / disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorMessage'
              example:
                message: Authorization header not found
        '403':
          description: >-
            Access denied - your current plan does not include the External
            Billing API. Upgrade to PRO or above.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorMessage'
              example:
                message: >-
                  External Billing API access requires a PRO plan. Error Code:
                  external_billing_api
        '404':
          description: Payment request not found or does not belong to your tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorMessage'
              example:
                message: Payment request not found
        '500':
          description: Internal server error. Contact support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorMessage'
              example:
                message: Internal server error
components:
  schemas:
    UpdatePaymentRequestBody:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - PENDING
            - PAID
            - FAILED
            - CANCELED
            - OVERDUE
          description: New status for the payment request. Required.
          example: PAID
        external_payment_id:
          type: string
          description: >-
            Optional external transaction reference (e.g. `txn_abc123` from your
            payment processor). Recommended when setting status to `PAID`.
          example: txn_abc123
    PaymentRequest:
      type: object
      properties:
        id:
          type: string
          description: Payment request ID.
          example: '10'
        client_id:
          type: string
          description: ID of the client this payment request belongs to.
          example: '42'
        client_name:
          type: string
          description: Display name of the client.
          example: Acme Corp
        subscription_id:
          type: string
          nullable: true
          description: >-
            ID of the associated subscription (null for non-subscription
            requests).
          example: '1'
        status:
          type: string
          enum:
            - PENDING
            - PAID
            - FAILED
            - CANCELED
            - OVERDUE
          description: Current payment request status.
          example: PENDING
        type:
          type: string
          enum:
            - SUBSCRIPTION
            - ADDON
            - OVERAGE
            - ONE_TIME
          description: Type of the payment request.
          example: SUBSCRIPTION
        amount:
          type: string
          description: Total amount as a decimal string.
          example: '99.00'
        currency:
          type: string
          description: 3-letter currency code.
          example: USD
        period_start:
          type: string
          format: date-time
          nullable: true
          description: Start of the billing period this request covers.
          example: '2025-01-01T00:00:00'
        period_end:
          type: string
          format: date-time
          nullable: true
          description: End of the billing period this request covers.
          example: '2025-02-01T00:00:00'
        due_date:
          type: string
          format: date-time
          nullable: true
          description: Date by which payment is due.
          example: '2025-01-15T00:00:00'
        grace_period_ends_at:
          type: string
          format: date-time
          nullable: true
          description: End of grace period (cycle requests only). Null for pack requests.
          example: '2025-01-22T00:00:00'
        paid_at:
          type: string
          format: date-time
          nullable: true
          description: Timestamp when the request was marked paid. Null if not yet paid.
          example: null
        external_payment_id:
          type: string
          nullable: true
          description: >-
            Optional external transaction reference (e.g. a payment processor
            transaction ID).
          example: null
        notes:
          type: string
          nullable: true
          description: Free-text notes on the request.
          example: null
        line_items:
          type: array
          items:
            type: object
          description: Breakdown of individual line items making up the total amount.
        created_at:
          type: string
          format: date-time
          description: When the request was created (ISO 8601).
          example: '2025-01-01T00:00:00'
        updated_at:
          type: string
          format: date-time
          description: When the request was last updated (ISO 8601).
          example: '2025-01-01T00:00:00'
    ValidationError:
      type: object
      properties:
        message:
          type: string
          example: Invalid status 'unknown'.
        valid_values:
          type: array
          items:
            type: string
          example:
            - PENDING
            - PAID
            - FAILED
            - CANCELED
            - OVERDUE
    ErrorMessage:
      type: object
      properties:
        message:
          type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````