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

# Merge Payee

> Repoints every non-deleted invoice linked to `{payeeId}` at `toPayeeId`,
in ONE transaction — the merge composition. Per linked invoice:
`payment_removed ×k → payee_changed → payment_recorded ×k` (invoices
without live payments take just the middle step). Re-recorded marks
copy amount / date / memo / line item / erode flag verbatim under NEW
server-minted payment ids — EXCEPT payments whose removal composed a
reserve unwind (reset-absorbed erosion), which re-record NON-eroding so
reserves are not eroded twice.

**Idempotent by convergence.** All member actions are server-derived (no
client `actionId`s, no `If-Match`): a retry finds nothing linked to the
merged payee and emits nothing (`journalIds: []`).

**Selector vs target.** `{payeeId}` is a pure selector — an id with no
linked invoices converges to an empty merge; `toPayeeId` must exist
(`422 LINK_TARGET_MISSING`) and must differ from `{payeeId}` (a
schema-level `400`).

**Required permission:** `company.payment:update`




## OpenAPI

````yaml /openapi/generated-external-api.yaml post /api/v1/companies/{companyId}/financials/payees/{payeeId}/merge
openapi: 3.0.3
info:
  title: AI Insurance External API
  description: External API for AI Insurance platform
  version: 1.0.0
  contact:
    email: support@aiinsurance.io
servers:
  - url: https://go.aiinsurance.io
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /api/v1/companies/{companyId}/financials/payees/{payeeId}/merge:
    post:
      tags:
        - Financials
      summary: Merge Payee
      description: |
        Repoints every non-deleted invoice linked to `{payeeId}` at `toPayeeId`,
        in ONE transaction — the merge composition. Per linked invoice:
        `payment_removed ×k → payee_changed → payment_recorded ×k` (invoices
        without live payments take just the middle step). Re-recorded marks
        copy amount / date / memo / line item / erode flag verbatim under NEW
        server-minted payment ids — EXCEPT payments whose removal composed a
        reserve unwind (reset-absorbed erosion), which re-record NON-eroding so
        reserves are not eroded twice.

        **Idempotent by convergence.** All member actions are server-derived (no
        client `actionId`s, no `If-Match`): a retry finds nothing linked to the
        merged payee and emits nothing (`journalIds: []`).

        **Selector vs target.** `{payeeId}` is a pure selector — an id with no
        linked invoices converges to an empty merge; `toPayeeId` must exist
        (`422 LINK_TARGET_MISSING`) and must differ from `{payeeId}` (a
        schema-level `400`).

        **Required permission:** `company.payment:update`
      operationId: mergeFinancialsPayee
      parameters:
        - $ref: '#/components/parameters/companyId'
        - name: payeeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: >-
            The payee being merged AWAY — a pure selector; every invoice linked
            to it is repointed
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - toPayeeId
              properties:
                toPayeeId:
                  type: string
                  format: uuid
                  description: >-
                    The surviving payee every linked invoice is repointed to;
                    must exist and differ from `{payeeId}`
                author:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: >-
                    Optional display label for the source system's author (e.g.
                    the integrator-side user). Stamped as the journal record's
                    display attribution; the acting principal stays the External
                    API service user, so a label can never impersonate an in-app
                    user. Ignored on idempotent replays
            examples:
              merge:
                summary: Merge one payee into another
                value:
                  toPayeeId: 550e8400-e29b-41d4-a716-446655440211
      responses:
        '200':
          description: >-
            Every emitted journal id, in execution order — empty on a converged
            retry (nothing left linked to the merged payee)
          content:
            application/json:
              schema:
                type: object
                required:
                  - journalIds
                properties:
                  journalIds:
                    type: array
                    items:
                      type: string
                      format: uuid
                    description: Every emitted journal id, in execution order
              examples:
                merged:
                  summary: One paid invoice repointed (remove -> repoint -> re-record)
                  value:
                    journalIds:
                      - 6d8e0f2a-4b5c-4d7e-9f1a-0b2c3d4e5f6a
                      - 7e9f1a3b-5c6d-4e8f-0a2b-1c3d4e5f6a7b
                      - 8f0a2b4c-6d7e-4f9a-1b3c-2d4e5f6a7b8c
                converged:
                  summary: A retry converges to an empty merge
                  value:
                    journalIds: []
        '400':
          description: >-
            Bad Request — a malformed payload or a self-merge (`toPayeeId` equal
            to the path payee)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                selfMerge:
                  summary: Self-merge rejected
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: 'toPayeeId: toPayeeId must differ from the merged payee'
                      userMessages:
                        - 'toPayeeId: toPayeeId must differ from the merged payee'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: >-
            Not Found — the company does not have this financials surface
            enabled (the endpoint behaves as if it does not exist). An unknown
            MERGED payee is not a 404: it converges to an empty merge
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  summary: Not found
                  value:
                    error:
                      code: NotFoundError
                      message: Not found
                      userMessages:
                        - Not found
        '422':
          $ref: '#/components/responses/FinancialsV2PreconditionFailed'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    companyId:
      name: companyId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Company identifier
  schemas:
    ErrorResponse:
      type: object
      description: Standard error response for all external API endpoints
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code
              example: VALIDATION_ERROR
            message:
              type: string
              description: Human-readable error message
              example: 'submissionId: Required field is missing'
            userMessages:
              type: array
              description: >-
                Clean, verbatim-displayable messages — one entry per failure,
                free of error-code tags, field paths, and internal noise.
                Suitable for showing to end users as-is.
              items:
                type: string
              example:
                - Exposures of type 'company' require an address
            details:
              type: array
              description: Additional details for validation errors (field-level errors)
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: The field that caused the error
                    example: submissionId
                  message:
                    type: string
                    description: Description of the field error
                    example: Required field is missing
    FinancialsV2PreconditionError:
      type: object
      description: >-
        A failed write precondition (`422`): the request was well-formed but the
        books reject it. Preconditions fail closed — a rejected write leaves no
        journal record, no ledger change, and no read-state change. `error.code`
        is one of the STABLE financials error codes (single source:
        `FINANCIALS_V2_PRECONDITION_CODES` in the engine's error module,
        re-exported to this surface as `FINANCIALS_V2_API_ERROR_CODES`) —
        messages may be reworded, the codes will not.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - LIVE_PAYMENTS
                - AMOUNT_EXCEEDS_BALANCE_DUE
                - INVALID_AMOUNT
                - LINE_ITEMS_IMMUTABLE
                - INVOICE_DELETED
                - INVOICE_VOIDED
                - INVOICE_DRAFT
                - INVOICE_NOT_DRAFT
                - PAYMENT_NOT_FOUND
                - ERODE_RESERVES_MISMATCH
                - ERODE_REQUIRES_EVENT
                - DEPRECATED_CONFIG
                - NOT_RESERVED_CATEGORY
                - FUTURE_DATE
                - UNKNOWN_ID
                - LINK_TARGET_MISSING
                - UNAPPROVE_PAID
                - NOT_APPROVED
                - POLICY_INVOICE_BATCH_ONLY
              description: >-
                The stable precondition code — one per guard. `LIVE_PAYMENTS`
                (the action — re-link, payee change, or void — needs the
                invoice's live payments removed first);
                `AMOUNT_EXCEEDS_BALANCE_DUE` (a payment mark's magnitude
                overshoots its line item's remaining — per line, never a
                document scalar); `INVALID_AMOUNT` (a zero mark, or a mark whose
                sign opposes its line item's open remaining — a `payBalanceDue`
                against a document with nothing open included);
                `LINE_ITEMS_IMMUTABLE` (a posted invoice's line items — ids,
                types, amounts — and its category are fixed from the posting
                moment: any post-posting change is rejected, at any payment
                count; corrections are void-and-recreate, payments removed
                first; drafts edit freely until finalize); `INVOICE_DELETED` /
                `INVOICE_VOIDED` (lifecycle closures); `INVOICE_DRAFT` (drafts
                are unposted — money may not touch one: recording a payment on
                or voiding a draft is rejected; finalize it first);
                `INVOICE_NOT_DRAFT` (finalize targets exactly a draft);
                `PAYMENT_NOT_FOUND` (no live payment mark with that id on that
                invoice); `ERODE_RESERVES_MISMATCH` / `ERODE_REQUIRES_EVENT`
                (the erode flag missing on a reserved category or present on
                operating; eroding needs a linked event — there is no headroom
                bound: the remaining reserve is a signed balance and may cross
                zero); `DEPRECATED_CONFIG` / `NOT_RESERVED_CATEGORY` /
                `UNKNOWN_ID` (cited configuration guards); `FUTURE_DATE`;
                `LINK_TARGET_MISSING` (the named link target does not exist);
                `UNAPPROVE_PAID` (an approval cannot be revoked once payments
                have been recorded — remove them first); `NOT_APPROVED` (money
                cannot post against an unapproved invoice AND the calling key
                does not hold `company.payment:approve`, so it cannot approve it
                either — a key that holds the permission auto-approves instead
                of failing here); `POLICY_INVOICE_BATCH_ONLY` (the write touches
                a POLICY-LINKED invoice's document — creating one with
                `links.policy`, or updating, voiding, restoring, deleting, or
                re-linking one that is already policy-linked. A policy's
                invoices are issued as one set so that every price component of
                the policy's pricing contract stays netted to its own line item,
                so they are written only by the policy invoice batch: `POST
                /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch`.
                Change the policy or submit an explicit batch and the invoices
                are reissued. Payments, payee changes, and approvals on a policy
                invoice are unaffected)
            message:
              type: string
              description: Human-readable description of the failed precondition
            userMessages:
              type: array
              description: Clean, verbatim-displayable messages
              items:
                type: string
  responses:
    Unauthorized:
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missingApiKey:
              summary: Missing API key
              value:
                error:
                  code: AuthenticationError
                  message: API key authentication required
                  userMessages:
                    - API key authentication required
            invalidApiKey:
              summary: >-
                Invalid API key (e.g. unknown key, or a Bearer token used
                instead of an API key)
              value:
                error:
                  code: AuthenticationError
                  message: Invalid API key
                  userMessages:
                    - Invalid API key
    Forbidden:
      description: Forbidden - Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            insufficientPermissions:
              summary: Insufficient permissions
              value:
                error:
                  code: AuthorizationError
                  message: User is not authorized to perform the requested action
                  userMessages:
                    - User is not authorized to perform the requested action
            companyMismatch:
              summary: A valid API key naming another company in the URL
              value:
                error:
                  code: AuthorizationError
                  message: API key is not scoped to the requested company
                  userMessages:
                    - API key is not scoped to the requested company
    FinancialsV2PreconditionFailed:
      description: >-
        Unprocessable — a write precondition failed. Fails closed: no journal
        record, no ledger change, no read-state change. `error.code` is one of
        the stable financials error codes; each operation's description names
        its signature codes.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/FinancialsV2PreconditionError'
          examples:
            livePayments:
              summary: The action requires the invoice's live payments removed first
              value:
                error:
                  code: LIVE_PAYMENTS
                  message: >-
                    The invoice has live payments; remove them before this
                    action
                  userMessages:
                    - >-
                      The invoice has live payments; remove them before this
                      action
            unknownId:
              summary: >-
                A cited configuration id is unknown (or outside the cited
                category)
              value:
                error:
                  code: UNKNOWN_ID
                  message: lineItemTypeId does not belong to the cited category
                  userMessages:
                    - lineItemTypeId does not belong to the cited category
            policyInvoiceBatchOnly:
              summary: >-
                Policy-linked invoice documents are changed only as one policy
                batch
              value:
                error:
                  code: POLICY_INVOICE_BATCH_ONLY
                  message: >-
                    Policy-linked invoice documents can only be changed through
                    POST
                    /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch
                  userMessages:
                    - >-
                      Policy-linked invoice documents can only be changed
                      through POST
                      /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch
    InternalServerError:
      description: Internal Server Error - Unexpected error occurred
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            internalError:
              summary: Unexpected server error
              value:
                error:
                  code: UncaughtActionError
                  message: Uncaught error occurred in <actionName>
                  userMessages:
                    - An unexpected error occurred. Please try again later.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        API key authentication. Send your raw API key as the `Authorization`
        header value with NO scheme prefix — `Authorization: YOUR-API-KEY`. Do
        NOT prefix it with `Bearer ` or `ApiKey `, and do not use an `X-API-Key`
        header; those are not accepted.

````