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

# Create a payout

> Pay a selection of line items and adjustments for one or more vendors (up to 50), one batch per vendor using a single payout method. Runs asynchronously: returns a `transactionId` to poll. Send a unique `Idempotency-Key` so retries are safe. Requires the `admin:payouts:make_payment` permission.



## OpenAPI

````yaml openapi-merchant.json POST /payouts/make-payment
openapi: 3.0.0
info:
  title: PuppetVendors Merchant API V2
  version: 2.0.0
  description: >-
    The endpoints reachable with a merchant API key (`mk_live_…`), minted from
    Settings → API Access in the PuppetVendors admin. A merchant key administers
    users and vendors, and renews its own token. Everything else in the V2 API,
    including orders, products, payouts, fulfillments and documents, stays on
    the v1 API token and is refused here with a 403. Keys are revealed once at
    creation; rotating one leaves the previous key working for a 24 hour grace
    window.
  contact:
    name: PuppetVendors Support
    url: https://puppetvendors.com
  license:
    name: Proprietary
servers:
  - url: https://production-api.puppetvendors.com
    description: Production
  - url: http://localhost:8082
    description: Local development
security:
  - bearerAuth: []
tags:
  - name: Auth
    description: Authentication endpoints for obtaining and refreshing API tokens
  - name: Portal - Auth
    description: Vendor portal authentication endpoints (vendor user login, token refresh)
  - name: Products
    description: Product management and synchronization
  - name: Orders
    description: Order retrieval and management
  - name: Payouts
    description: Vendor payout information
  - name: Fulfillments
    description: Order fulfillment operations
  - name: Reports
    description: Vendor sales reports and analytics
  - name: Vendors
    description: Vendor management operations
  - name: Settings
    description: Settings endpoints for vendor configuration and management
  - name: Users
    description: User account management
  - name: Shop
    description: Shop configuration and settings
  - name: Commissions
    description: Commission rate management
  - name: Integrations
    description: Vendor integration configuration endpoints
  - name: Line Items
    description: Order line item operations
paths:
  /payouts/make-payment:
    post:
      tags:
        - Payouts
      summary: Create a payout
      description: >-
        Pay a selection of line items and adjustments for one or more vendors
        (up to 50), one batch per vendor using a single payout method. Runs
        asynchronously: returns a `transactionId` to poll. Send a unique
        `Idempotency-Key` so retries are safe. Requires the
        `admin:payouts:make_payment` permission.
      operationId: makePayoutPayment
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 128
          description: >-
            Caller-chosen dedupe key, unique per payout you intend to make.
            Retrying the same request with the same key returns the same
            requestId and never starts a second run; the engine deduplicates on
            it. The key is bound to its request body: the same key with a
            different body is refused with 422 IDEMPOTENCY_KEY_REUSED. A NEW key
            is a NEW payout: always retry a timed-out or failed-to-connect
            request with its original key, never a fresh one, or the same items
            can be paid twice.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - vendors
                - method
              properties:
                vendors:
                  type: array
                  minItems: 1
                  maxItems: 50
                  description: >-
                    The vendors to pay, one group each, paid as one batch per
                    vendor. A vendorId may appear only once, and a line item or
                    adjustment id under only one vendor (ids compare
                    case-insensitively). Every vendor must belong to this shop
                    and be ready for the method, or the whole request is refused
                    and nothing is paid.
                  items:
                    type: object
                    required:
                      - vendorId
                    properties:
                      vendorId:
                        type: string
                        description: >-
                          The vendor being paid (24-char hex ObjectId), must
                          belong to this shop
                        example: 507f1f77bcf86cd799439012
                      lineItemIds:
                        type: array
                        default: []
                        items:
                          type: string
                        description: >-
                          This vendor's line items to pay (24-char hex
                          ObjectIds; type lineItem in GET
                          /payouts/pending/items/{vendorId}). Send at least one
                          line item or one adjustment per vendor. A line item of
                          another vendor is refused with ITEM_VENDOR_MISMATCH.
                        example:
                          - 507f1f77bcf86cd799439099
                      adjustmentIds:
                        type: array
                        default: []
                        items:
                          type: string
                        description: >-
                          This vendor's adjustments to pay in the same batch
                          (24-char hex ObjectIds; type customItem in GET
                          /payouts/pending/items/{vendorId}). Each must be this
                          vendor's, unpaid and not excluded from payouts, or the
                          request is refused with ADJUSTMENTS_NOT_PAYABLE. May
                          be sent without lineItemIds for an adjustments-only
                          payout.
                        example:
                          - 507f1f77bcf86cd799439088
                method:
                  type: string
                  enum:
                    - stripe
                    - paypal
                    - globalPayouts
                    - manual
                  description: >-
                    READ-side method name, one for every vendor of the run;
                    mapped to legacy resolver keys at the bridge. manual records
                    a payment made outside the app and needs manualMethod
                manualMethod:
                  type: string
                  enum:
                    - bankTransfer
                    - paypal
                    - cash
                    - other
                  description: >-
                    How a manual payout was made, the same options as the
                    merchant portal. Recorded as the run's payout method and
                    shown in the vendor email. Required when method is manual,
                    refused with any other method
                  example: bankTransfer
                notifyVendor:
                  type: boolean
                  default: true
                  description: Whether the run emails the vendor
                payoutDate:
                  anyOf:
                    - type: string
                      format: date-time
                    - type: string
                      format: date
                  description: >-
                    Recorded as the payout date. Either an RFC 3339 date-time or
                    a calendar date without a time (YYYY-MM-DD), recorded as
                    that day in the merchant portal's MM/DD/YYYY. When omitted,
                    defaults to today in the shop's timezone (as MM/DD/YYYY), as
                    the merchant portal pre-fills it
                  example: '2026-03-02'
                comment:
                  type: string
                  maxLength: 2000
                  description: Free-text note recorded with the run
            example:
              vendors:
                - vendorId: 507f1f77bcf86cd799439012
                  lineItemIds:
                    - 507f1f77bcf86cd799439099
                    - 507f1f77bcf86cd79943909a
                  adjustmentIds:
                    - 507f1f77bcf86cd799439088
                - vendorId: 507f1f77bcf86cd799439013
                  lineItemIds:
                    - 507f1f77bcf86cd7994390b1
              method: stripe
              notifyVendor: true
              payoutDate: '2026-03-02'
              comment: March payouts
      responses:
        '200':
          description: >-
            Replay of a run that already completed under this Idempotency-Key;
            nothing new was started and the stored result is returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      requestId:
                        type: string
                      status:
                        type: string
                        enum:
                          - completed
                      replay:
                        type: boolean
                        enum:
                          - true
                      transactionId:
                        type: string
                        nullable: true
                      totalPayout:
                        type: number
                        nullable: true
        '202':
          description: >-
            Run accepted; executing asynchronously. Poll GET
            /payouts/make-payment/{transactionId} for the batches.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      requestId:
                        type: string
                        description: The Action row id (a support reference)
                      transactionId:
                        type: string
                        nullable: true
                        description: >-
                          Pre-generated; the poll key for GET
                          /payouts/make-payment/{transactionId}
                      status:
                        type: string
                        example: processing
                      dryRun:
                        type: boolean
                        enum:
                          - false
                      vendors:
                        type: array
                        description: >-
                          Accept-time totals, same row shape as the dry-run
                          response
                        items:
                          type: object
                          properties:
                            vendorId:
                              type: string
                            vendorName:
                              type: string
                            totalAmount:
                              type: number
                            totalLineItemsCount:
                              type: integer
                            issue:
                              type: string
                              nullable: true
                      totalPayout:
                        type: number
                        nullable: true
        '400':
          description: >-
            Missing/short Idempotency-Key, or an invalid body (no vendors or
            more than 50, a vendor with no line items or adjustments selected,
            the same vendorId twice, a line item or adjustment id under two
            vendors, non-hex ids, the old top-level vendorId / lineItemIds /
            adjustmentIds sent instead of vendors[], customItemIds sent instead
            of adjustmentIds, unknown method, manual without manualMethod or
            manualMethod with another method, payoutDate neither a YYYY-MM-DD
            date nor an RFC 3339 date-time)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            Forbidden - merchant scope with admin:payouts:make_payment required
            (admin:payouts alone is NOT enough), or
            MERCHANT_PAYOUTS_API_NOT_ENABLED (the payouts API is not enabled for
            this store)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            Some vendors are not in this shop (NOT_FOUND, details.vendorIds
            lists every one; another shop's vendorId reads the same as an
            unknown one). Nothing is paid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            A run with this Idempotency-Key is already in flight
            (IDEMPOTENCY_IN_PROGRESS; details.transactionId is the poll key,
            poll it instead of retrying), or another payout run is paying some
            of these line items right now (PAYOUT_ITEMS_LOCKED,
            details.conflicts lists them; nothing was started and the key is not
            used up; wait for that run, then check what is still unpaid)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            The Idempotency-Key was already used for a different request
            (IDEMPOTENCY_KEY_REUSED, details.requestId is the run it was used
            for; nothing is started), or the selection cannot be paid and the
            whole request is refused, no vendor is paid
            (PAYOUT_METHOD_UNAVAILABLE, VENDOR_NOT_READY_FOR_METHOD
            (details.vendorIds lists every vendor not ready for the method),
            VENDOR_HIDDEN (details.vendorIds lists every vendor the merchant has
            hidden; hidden vendors are not payable), or an engine rejection
            whose code and details pass through verbatim, such as
            LINE_ITEMS_NOT_FOUND, ITEM_VENDOR_MISMATCH (a line item listed under
            a vendor it does not belong to; details.otherVendor and
            details.noVendor list the line item ids, or, when the dry run
            resolved a vendor that was not requested, details.requested and
            details.resolved list the vendorIds), ADJUSTMENTS_NOT_PAYABLE
            (details lists the refused ids under notFound, otherVendor,
            notUnpaid and excludedFromPayout), PAYOUT_NO_ITEMS, or
            PAYOUT_VALIDATION_FAILED (details.issues lists each vendor's issue
            as { vendorId, issue }, or details.vendorIds names vendors with
            nothing payable))
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
components:
  schemas:
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          enum:
            - false
          description: Indicates the request failed
        error:
          type: object
          required:
            - message
          properties:
            message:
              type: string
              description: Human-readable error message
              example: Resource not found
            code:
              type: string
              description: >-
                Stable machine-readable error code. The frontend uses this as
                its localisation key (e.g. errors.api.<code>). Server errors
                always report INTERNAL_SERVER_ERROR.
              example: NOT_FOUND
            errorId:
              type: string
              description: >-
                Correlation id for support/log lookup. Present on opaque 5xx
                responses so a user can quote a reference for the underlying
                (server-side logged) error.
              example: err_1783365360839_j281w7b
            details:
              type: object
              description: >-
                Additional client-error context (e.g. field-level validation
                errors). Never populated for server errors.
  securitySchemes:
    bearerAuth:
      type: apiKey
      in: header
      name: x-access-token
      description: >-
        JWT minted from a merchant API key (`mk_live_…`) by POST /authenticate.
        Send the token value directly, with no "Bearer" prefix. Tokens last 14
        days and can be renewed at POST /refresh-token; revoking the key refuses
        the next request made with any token minted from it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.