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

# Void a posted customer receipt

> Takes a POSTED customer cash receipt back off the books: reverses its journal entry, removes it from every invoice it was paying so those balances are owed again, puts any credit-terms promise it had settled back to open, marks the receipt voided, and hands every bank line it was matched to back to the review queue so the money can be recorded correctly.
USE IT WHEN THE RECORDING WAS WRONG, not when the money came back. A refund moves real cash and posts its own entries; a void says this receipt should never have been recorded (it was keyed against the wrong customer, or it was the second recording of one bank line, or the deposit never cleared).
THE REVERSAL IS DATED AT THE ENTRY IT REVERSES, not at today. The cash never left the bank, and the bank line goes straight back to the review queue still carrying its own statement date, so the correction belongs in the month the money moved in. Dating it today would leave the receipt sitting in the books for every date in between and a reconciled month would stop tying. Pass relief_entry_date only when you specifically need the reversal somewhere else; it is recorded on the audit row as an explicit choice.
A CLOSED PERIOD REFUSES (422 period_closed) and names both the closed period and the next open one. It never rolls the correction forward into a month the cash did not move in.
CARD RECEIPTS ARE NOT VOIDED HERE (422 receipt_void_is_the_processors). Captured card money comes back through a refund at the processor; an uncaptured authorization is released through POST /v1/payments/{payment_intent_id}/void. Reversing the ledger here would tell the books the money returned while it is still with the processor.
Idempotent via the Idempotency-Key header. Entity-scoped: a receipt id outside the calling key's entity answers 404.




## OpenAPI

````yaml /openapi.yaml post /customer-payments/{id}/void
openapi: 3.1.0
info:
  title: Arcus ERP Public API
  version: 1.0.0
  description: >
    Arcus ERP public REST API. Designed for external integrations and data
    migration.


    **Authentication.** Bearer token (API key) via the `Authorization` header.

    Format: `Authorization: Bearer ark_live_ent_<code>_<random>` (or
    `ark_test_*` for sandbox).

    API keys are issued per-entity in **Settings > Developers > API Keys**.


    **Entity scoping.** The entity is encoded in the API key prefix; routes are
    flat

    (e.g. `/v1/accounts`, `/v1/orders`, `/v1/products`). A small set of platform
    endpoints

    (migration, reconciliation, events, webhook endpoints, API keys) use the

    `/v1/entities/{entity_id}/...` form -- those are noted in their tags.


    **Key capabilities.**
      - Related-resource hydration via `?expand[]=` (see `x-arcus-expand` on each resource).
      - Cursor-based pagination (`starting_after` / `ending_before` / `limit`).
      - Idempotency via the `Idempotency-Key` header.
      - Webhook events for asynchronous notification.
      - Conditional requests / ETag for cache validation.

    **Changelog.** Entries are dated and name every published contract whose
    MEANING moved, not

    only the ones whose field names changed. The narrative version of the same
    entries, written

    for integrators, is published at https://arcuserp.mintlify.app/changelog.


    **2026-09-22 (planned 2026-09-21), REORDER-BUYER-TRUTH: demand changed what
    it MEANS on three

    published contracts, with no field renamed.** An integrator that pins field
    names sees no

    breakage and different numbers, which is why this entry exists.

      - **Demand now counts build consumption.** `demand_avg_per_day` on the public product, kit
        and inventory-balance objects, and `daily_demand` / `demand_basis` / `net_suggested_qty`
        on `GET /v1/purchasing/reorder-report`, are composed from fulfilled sales lines PLUS
        posted `build_consume` inventory draws: each physical decrement of a product counts
        exactly once. The previous rule adopted an internal-consumption basis only when the sales
        blend was exactly zero, so a product both sold AND consumed into work orders planned as
        if the build draws did not exist. Products drawn into work orders move; on one
        production-shaped dataset five did, the largest from 0.05/day to 14.95/day.
      - **`demand_basis` now carries four values, not two:** `sales`, `sales_and_builds`,
        `builds` and `consumption`. A consumer with a two-branch reader (anything that is not
        `consumption` is `sales`) silently hides the two new ones.
      - **`current_demand_units` on `GET /v1/inventory/balances/:id` moved, by a second rule.**
        It counts committed-but-unshipped CUSTOMER demand, and it now counts a kit component's
        own line rather than its parent kit line, and excludes non-sales documents. On one
        production dataset 269 of 1,043 balance rows changed, 227 of them downward; the largest
        single move was 1,941 to 25, on a product whose open PURCHASE order line had been
        reported as customer demand. Re-baseline anything that alerts or reorders off this field.
      - `run_rate_30/90/180/365` and `blended_daily` on the same balance object move for the
        first reason above.
      - **Added, not changed:** `GET /v1/purchasing/reorder-report` accepts `demand_window` and
        returns the unit, cover-through and reorder-point-provenance fields documented on that
        operation; `order_multiple` is accepted and returned on the product-vendor doors.
      - **Volume note.** The `inventory.low_stock`, `inventory.out_of_stock` and
        `inventory.back_in_stock` webhook events are population-gated on
        `on_hand <= reorder_point`, and reorder points move with the demand above, so
        subscribers should expect a one-time step change in event volume around the release.
servers:
  - url: https://api.arcuserp.com/v1
    description: Arcus ERP API (accepts both live `ark_live_*` and test `ark_test_*` keys)
  - url: https://dev-api.arcuserp.com/v1
    description: >-
      Dev sandbox API (test-only data, accepts `ark_test_*` keys against dev
      RDS)
security: []
paths:
  /customer-payments/{id}/void:
    post:
      tags:
        - Payments
      summary: Void a posted customer receipt
      description: >
        Takes a POSTED customer cash receipt back off the books: reverses its
        journal entry, removes it from every invoice it was paying so those
        balances are owed again, puts any credit-terms promise it had settled
        back to open, marks the receipt voided, and hands every bank line it was
        matched to back to the review queue so the money can be recorded
        correctly.

        USE IT WHEN THE RECORDING WAS WRONG, not when the money came back. A
        refund moves real cash and posts its own entries; a void says this
        receipt should never have been recorded (it was keyed against the wrong
        customer, or it was the second recording of one bank line, or the
        deposit never cleared).

        THE REVERSAL IS DATED AT THE ENTRY IT REVERSES, not at today. The cash
        never left the bank, and the bank line goes straight back to the review
        queue still carrying its own statement date, so the correction belongs
        in the month the money moved in. Dating it today would leave the receipt
        sitting in the books for every date in between and a reconciled month
        would stop tying. Pass relief_entry_date only when you specifically need
        the reversal somewhere else; it is recorded on the audit row as an
        explicit choice.

        A CLOSED PERIOD REFUSES (422 period_closed) and names both the closed
        period and the next open one. It never rolls the correction forward into
        a month the cash did not move in.

        CARD RECEIPTS ARE NOT VOIDED HERE (422 receipt_void_is_the_processors).
        Captured card money comes back through a refund at the processor; an
        uncaptured authorization is released through POST
        /v1/payments/{payment_intent_id}/void. Reversing the ledger here would
        tell the books the money returned while it is still with the processor.

        Idempotent via the Idempotency-Key header. Entity-scoped: a receipt id
        outside the calling key's entity answers 404.
      operationId: voidCustomerPaymentVoid
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The order_payments id of the receipt to void.
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              properties:
                reason:
                  type: string
                  description: >
                    REQUIRED. Why the receipt is being voided. Recorded on the
                    receipt, on every invoice that gets its balance back, and on
                    the reversing journal entry. Omitting it answers 422
                    void_reason_required.
                relief_entry_date:
                  type: string
                  format: date
                  nullable: true
                  description: >
                    OPTIONAL explicit date for the reversing entry. Leave it out
                    and the reversal takes the original entry's own date, which
                    is what keeps a reconciled month tied. Whatever date is used
                    must fall in an open period.
      responses:
        '200':
          description: The receipt was voided, with the full unwind summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      payment_id:
                        type: string
                        format: uuid
                      account_id:
                        type: string
                        format: uuid
                        nullable: true
                      status:
                        type: string
                        example: voided
                      receipt_amount:
                        type: number
                      payment_method:
                        type: string
                      void_summary:
                        type: object
                        description: >-
                          Everything the void changed, so an integration can
                          show it without a second read.
                        properties:
                          invoices_reopened:
                            type: array
                            description: >-
                              The invoices that owe money again, and how much
                              each one got back.
                            items:
                              type: object
                              properties:
                                order_id:
                                  type: string
                                  format: uuid
                                document_number:
                                  type: string
                                amount_returned_to_balance:
                                  type: number
                          applications_removed:
                            type: array
                            description: The application rows as they were before the void.
                            items:
                              type: object
                              properties:
                                order_id:
                                  type: string
                                  format: uuid
                                document_number:
                                  type: string
                                amount_applied:
                                  type: number
                          applications_total_before:
                            type: number
                          journal_entry_id:
                            type: string
                            format: uuid
                            nullable: true
                            description: >-
                              The receipt's original entry. It is left posted
                              and is reversed by a contra.
                          reversal_journal_entry_id:
                            type: string
                            format: uuid
                            nullable: true
                          reversal_entry_number:
                            type: string
                            nullable: true
                          relief_entry_date:
                            type: string
                            format: date
                            nullable: true
                            description: The date the reversing entry carries.
                          credit_memo_id:
                            type: string
                            format: uuid
                            nullable: true
                          credit_memo_voided:
                            type: boolean
                            description: >-
                              True when the receipt had been held as a credit on
                              account and that credit was voided instead of a
                              bare entry being reversed.
                          terms_placements_reopened:
                            type: array
                            description: Invoices whose credit-terms promise is live again.
                            items:
                              type: object
                              properties:
                                order_id:
                                  type: string
                                  format: uuid
                                placement_ids:
                                  type: array
                                  items:
                                    type: string
                                    format: uuid
                          bank_transactions_returned_to_review:
                            type: array
                            description: >-
                              The bank lines that are back in the review queue,
                              on their own dates.
                            items:
                              type: object
                              properties:
                                bank_transaction_id:
                                  type: string
                                  format: uuid
                                bank_account_id:
                                  type: string
                                  format: uuid
                                transaction_date:
                                  type: string
                                  format: date
                          book_side:
                            type: object
                            nullable: true
                            description: The book half of the match that was un-stamped.
                      applications:
                        type: array
                        description: >-
                          The receipt's applications after the void. Always
                          empty.
                        items:
                          type: object
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/Error'
      security:
        - ApiKeyAuth: []
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: |
        Client-generated unique key for idempotent POST/PATCH/DELETE operations.
        Alias for the Idempotency parameter. Max 255 chars. On retry with the
        same key, the original response is returned without re-executing the
        operation. Keys expire after 24 hours.
      schema:
        type: string
        maxLength: 255
  responses:
    Error:
      description: Error response (400/401/403/404/409/422/429/500)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  schemas:
    ErrorEnvelope:
      type: object
      description: |
        Canonical error response envelope. All API errors use this shape.
      required:
        - error
        - code
      properties:
        error:
          type: string
          description: Machine-readable error key
          example: not_found
        code:
          type: string
          description: Machine-readable error code (often same as error)
          example: not_found
        type:
          type: string
          enum:
            - validation_error
            - permission_error
            - not_found
            - conflict
            - rate_limit
            - internal
            - expand_error
            - not_implemented
          example: not_found
        hint:
          type: string
          description: Human-readable one-sentence explanation (English)
          example: >-
            The requested order does not exist or does not belong to this
            entity.
        param:
          type: string
          description: The parameter that caused the error, if applicable
          example: expand[0]
        required:
          type: string
          description: The scope required (only on insufficient_scope errors)
          example: accounts:read
        request_id:
          type: string
          description: >-
            Unique request ID for support tracing (maps to CloudWatch log
            stream)
          example: req_abc123
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      description: |
        API key issued per entity via Settings > Developers > API Keys.
        Each key carries scopes (e.g. orders:read, products:write).
        Bearer token format: Authorization: Bearer ark_live_ent_<code>_<random>
        Test keys use ark_test_ent_<code>_<random>. Both are issued per entity
        via Settings > Developers > API Keys.

````