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

# Create a PIX transaction

> Creates an outbound PIX payment. What happens next depends on where the payee is, and the two branches answer with different statuses and different bodies — a client has to handle both.

An on-us payee (payee.ispb equal to this participant's own ISPB, on a direct order) settles SYNCHRONOUSLY as an INTRA_PSP ledger transfer and answers 201 with the transaction already EXECUTED. Any other payee settles over SPI in TWO PHASES: the amount is first HELD on the paying position as a pending ledger debit, and only then is the order submitted to JDPI (§8.4.1), which is asynchronous by design — nothing has settled when the call returns. A direct order answers 201 with the transaction PENDING; an order routed through a registered indirect (indirectId in the body) answers 202 carrying JD's own §8.4.1 acknowledgement byte-for-byte, whose idReqJdPi is the handle the outcome is read with. Either way, poll GET /v1/transactions/{transactionId}: a background reconciler consults JDPI §8.4.2 and then commits or cancels the hold, and the money is not settled until the status says so.

WHAT indirectId ACTUALLY MOVES, because it is more than a label. This participant may serve INDIRECT participants — other institutions registered here with POST /v1/indirects that reach SPI through this participant's ISPB. Naming one on an order changes THREE things at once: the amount is held on that institution's `@pi_{ispb}` settlement position instead of on the payer's own, the persisted order is stamped with that institution so it appears in GET /v1/indirects/{indirectId}/transactions, and the response shape changes from 201-plus-plugin-body to 202-plus-JD-body. The identifier is ROUTING DATA, not a credential: this participant authenticates you, and guaranteeing that the order really belongs to that institution is your responsibility, not this API's.

⚠️ indirectId IS SILENTLY IGNORED WHILE THIS TENANT'S INDIRECT-PARTICIPANTS FEATURE IS SWITCHED OFF. With the feature off the value is not parsed, not resolved and not refused: the order runs as a DIRECT one, holding the amount on the direct participant's own position and answering 201. Nothing in the response says the routing was dropped. Confirm the feature is on before sending the first order for an institution — an unknown id answering 404 PIX-0095 is the evidence that it is.

When the order settles, an indirect order ALSO pushes a NOTICE to the endpoint that registration carries (delivery.endpointUrl), once the background reconciler has JD's terminal outcome — an HTTPS POST forwarding JD's own result body verbatim, carrying X-Lerian-Signature ('sha256=' followed by the hex HMAC-SHA256 of the exact body bytes under the registration's shared secret) and X-Lerian-Timestamp. Delivery is AT-LEAST-ONCE and can also fail permanently while the payment stands, so the institution must dedup on the end-to-end id and treat GET /v1/indirects/{indirectId}/transactions, not the notice, as the record of truth. A direct order pushes no notice.

End-to-end ids: send endToEndId only when you already hold one (from a key lookup or a QR decode), and then payee.key is mandatory with it. Omit it on a manually typed order and SPI mints one — the response carries the minted value. Idempotency: the optional clientRequestId is the only identifier that exists BEFORE the JD call, so it is what makes a retry safe on every branch, and it is the ONLY protection a manually typed order has (that order's end-to-end id does not exist yet when the retry arrives). Amounts are integer centavos.

Every refusal below is decided before the hold is taken, so it moves ZERO money; when JDPI rejects the order after the hold exists, the hold is released and nothing is persisted. Refusals: 409 PIX-0029 when this endToEndId or clientRequestId was already used; 409 PIX-0076 when the paying position lacks the amount; 409 PIX-0078 on a transfer to the same account; 409 PIX-0032 when the account's period limit is exceeded; 422 PIX-0030 on a non-positive amount; 422 PIX-0037 when endToEndId arrives without payee.key; 422 PIX-0108 when the payer — or, on an indirect order, the indirect's `@pi` — resolves in the CRM but is not bound to a ledger account; 404 PIX-0095 for an unknown or malformed indirectId and 422 PIX-0100 when the indirect it names is not ACTIVE; 422 PIX-0102 when a payer.ispb is supplied on an indirect order and disagrees with that indirect's own ISPB; 400 PIX-0061 when accountId is empty or an accountType is outside the PIX domain 0..4. On an indirect order a JD business rejection is passed back with JD's own status and body instead of a problem+json.



## OpenAPI

````yaml /en/openapi/v3-current/pix.yaml post /v1/transactions
openapi: 3.1.0
info:
  description: Brazilian PIX Direct (DICT + SPI) plugin API.
  title: plugin-br-pix-jd
  version: 1.0.0
servers: []
security:
  - BearerAuth: []
paths:
  /v1/transactions:
    post:
      tags:
        - Transactions
      summary: Create a PIX transaction
      description: >-
        Creates an outbound PIX payment. What happens next depends on where the
        payee is, and the two branches answer with different statuses and
        different bodies — a client has to handle both.


        An on-us payee (payee.ispb equal to this participant's own ISPB, on a
        direct order) settles SYNCHRONOUSLY as an INTRA_PSP ledger transfer and
        answers 201 with the transaction already EXECUTED. Any other payee
        settles over SPI in TWO PHASES: the amount is first HELD on the paying
        position as a pending ledger debit, and only then is the order submitted
        to JDPI (§8.4.1), which is asynchronous by design — nothing has settled
        when the call returns. A direct order answers 201 with the transaction
        PENDING; an order routed through a registered indirect (indirectId in
        the body) answers 202 carrying JD's own §8.4.1 acknowledgement
        byte-for-byte, whose idReqJdPi is the handle the outcome is read with.
        Either way, poll GET /v1/transactions/{transactionId}: a background
        reconciler consults JDPI §8.4.2 and then commits or cancels the hold,
        and the money is not settled until the status says so.


        WHAT indirectId ACTUALLY MOVES, because it is more than a label. This
        participant may serve INDIRECT participants — other institutions
        registered here with POST /v1/indirects that reach SPI through this
        participant's ISPB. Naming one on an order changes THREE things at once:
        the amount is held on that institution's `@pi_{ispb}` settlement
        position instead of on the payer's own, the persisted order is stamped
        with that institution so it appears in GET
        /v1/indirects/{indirectId}/transactions, and the response shape changes
        from 201-plus-plugin-body to 202-plus-JD-body. The identifier is ROUTING
        DATA, not a credential: this participant authenticates you, and
        guaranteeing that the order really belongs to that institution is your
        responsibility, not this API's.


        ⚠️ indirectId IS SILENTLY IGNORED WHILE THIS TENANT'S
        INDIRECT-PARTICIPANTS FEATURE IS SWITCHED OFF. With the feature off the
        value is not parsed, not resolved and not refused: the order runs as a
        DIRECT one, holding the amount on the direct participant's own position
        and answering 201. Nothing in the response says the routing was dropped.
        Confirm the feature is on before sending the first order for an
        institution — an unknown id answering 404 PIX-0095 is the evidence that
        it is.


        When the order settles, an indirect order ALSO pushes a NOTICE to the
        endpoint that registration carries (delivery.endpointUrl), once the
        background reconciler has JD's terminal outcome — an HTTPS POST
        forwarding JD's own result body verbatim, carrying X-Lerian-Signature
        ('sha256=' followed by the hex HMAC-SHA256 of the exact body bytes under
        the registration's shared secret) and X-Lerian-Timestamp. Delivery is
        AT-LEAST-ONCE and can also fail permanently while the payment stands, so
        the institution must dedup on the end-to-end id and treat GET
        /v1/indirects/{indirectId}/transactions, not the notice, as the record
        of truth. A direct order pushes no notice.


        End-to-end ids: send endToEndId only when you already hold one (from a
        key lookup or a QR decode), and then payee.key is mandatory with it.
        Omit it on a manually typed order and SPI mints one — the response
        carries the minted value. Idempotency: the optional clientRequestId is
        the only identifier that exists BEFORE the JD call, so it is what makes
        a retry safe on every branch, and it is the ONLY protection a manually
        typed order has (that order's end-to-end id does not exist yet when the
        retry arrives). Amounts are integer centavos.


        Every refusal below is decided before the hold is taken, so it moves
        ZERO money; when JDPI rejects the order after the hold exists, the hold
        is released and nothing is persisted. Refusals: 409 PIX-0029 when this
        endToEndId or clientRequestId was already used; 409 PIX-0076 when the
        paying position lacks the amount; 409 PIX-0078 on a transfer to the same
        account; 409 PIX-0032 when the account's period limit is exceeded; 422
        PIX-0030 on a non-positive amount; 422 PIX-0037 when endToEndId arrives
        without payee.key; 422 PIX-0108 when the payer — or, on an indirect
        order, the indirect's `@pi` — resolves in the CRM but is not bound to a
        ledger account; 404 PIX-0095 for an unknown or malformed indirectId and
        422 PIX-0100 when the indirect it names is not ACTIVE; 422 PIX-0102 when
        a payer.ispb is supplied on an indirect order and disagrees with that
        indirect's own ISPB; 400 PIX-0061 when accountId is empty or an
        accountType is outside the PIX domain 0..4. On an indirect order a JD
        business rejection is passed back with JD's own status and body instead
        of a problem+json.
      operationId: createTransaction
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTransactionBody'
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateTransactionResponse'
          description: Created
        '202':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateTransactionAcceptedResponse'
          description: >-
            Accepted for asynchronous settlement. Returned on the arrangement
            (indirect-participant) path, where the body is JD's own §8.4.1
            acknowledgement passed through verbatim. Nothing has settled: the
            amount is reserved on the ordering participant's position and the
            outcome is obtained by polling JDPI §8.4.2 with the returned
            idReqJdPi.
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Unprocessable Entity
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Internal Server Error
        default:
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Error
components:
  schemas:
    CreateTransactionBody:
      additionalProperties: false
      properties:
        accountId:
          description: The CRM account id of the payer.
          examples:
            - acc-123
          type: string
        amount:
          description: Transaction amount in centavos (an integer, e.g. 10050 = R$ 100,50).
          examples:
            - 10050
          format: int64
          type: integer
        clientRequestId:
          description: >-
            Optional client idempotency token. A retry carrying the same value
            is rejected instead of moving money twice; it also seeds the Midaz
            hold key and the JDPI Chave-Idempotencia. Send it on a
            manually-typed order: that order has no endToEndId until SPI mints
            it, so this token is the only thing that can dedup a retry. Omit to
            opt out.
          examples:
            - req-2025-01-01-abc123
          type: string
        description:
          description: Free-text payment description.
          examples:
            - Invoice 42
          type: string
        endToEndId:
          description: >-
            SPI end-to-end id. Omit ONLY for a manually-typed payment order: per
            JDPI §8.4.1 the id does not exist yet and is generated by SPI on
            this call. If you obtained one from decode-QRCode (§8.3.3) or
            consultar-chave (§8.2.14) you MUST send it — omitting it makes SPI
            mint a DIFFERENT id, which will not match the payee's QR/txId
            conciliation.
          examples:
            - E1234567890
          type: string
        indirectId:
          description: >-
            Optional registered-indirect identifier (UUID) routing this outbound
            order through an indirect participant's PIX position. Omit for a
            direct order. Validated server-side before any money moves; unknown
            or inactive is rejected.
          examples:
            - 3f2504e0-4f89-41d3-9a0c-0305e82c3301
          format: uuid
          type: string
        key:
          description: The payer's PIX key, when paying from a key.
          examples:
            - payer@bar.com
          type: string
        payee:
          $ref: '#/components/schemas/PayeeBody'
          description: The recipient bank data.
        priority:
          description: Numeric eTransactionPriority (1=PRIMARY,2=SECONDARY).
          examples:
            - 1
          format: int64
          type: integer
      required:
        - accountId
        - amount
        - payee
      type: object
    CreateTransactionResponse:
      additionalProperties: false
      properties:
        amount:
          description: Transaction amount in centavos.
          examples:
            - 10050
          format: int64
          type: integer
        description:
          description: Payment description.
          examples:
            - Invoice 42
          type: string
        endToEndId:
          description: The SPI end-to-end id.
          examples:
            - E1234567890
          type: string
        flow:
          description: Numeric eTransactionFlow (2=CASH_OUT,3=INTRA_PSP).
          examples:
            - 2
          format: int64
          type: integer
        id:
          description: The created transaction id.
          examples:
            - d1f9c0a2-...
          type: string
        jdpiRequestId:
          description: The JDPI request id (external CASH_OUT).
          examples:
            - REQ-123
          type: string
        priority:
          description: Numeric eTransactionPriority.
          examples:
            - 1
          format: int64
          type: integer
        status:
          description: eTransactionStatus name.
          examples:
            - PENDING
          type: string
      required:
        - id
        - status
        - amount
      type: object
    CreateTransactionAcceptedResponse:
      additionalProperties: true
      properties:
        dtHrReqJdPi:
          description: The JD request timestamp.
          examples:
            - '2026-01-01T00:00:00.000Z'
          type: string
        endToEndId:
          description: The SPI end-to-end id JD assigned to the order.
          examples:
            - E1234567890
          type: string
        idReqJdPi:
          description: >-
            The JD request id. Poll JDPI §8.4.2 with this id to learn the
            settlement outcome.
          examples:
            - 45F763C6-7487-4363-9406-A1DE2A9636D3
          type: string
        idReqSistemaCliente:
          description: The client request id JD echoes back (the idempotency key, GUID).
          examples:
            - 9F4C2A10-0000-4000-8000-000000000001
          type: string
        tpCanal:
          description: The JD settlement-channel discriminator, when JD sends it.
          examples:
            - 2
          format: int64
          type: integer
      required:
        - idReqSistemaCliente
        - idReqJdPi
        - endToEndId
        - dtHrReqJdPi
      type: object
    Detail:
      additionalProperties: false
      properties:
        code:
          description: >-
            Stable, machine-readable domain error code scoped to the emitting
            service (format: <SERVICE>-NNNN).
          examples:
            - ERR-0001
          type: string
        detail:
          description: >-
            A human-readable explanation specific to this occurrence of the
            problem.
          examples:
            - Property foo is required but is missing.
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: '#/components/schemas/ErrorDetail'
          type:
            - array
            - 'null'
        instance:
          description: >-
            A URI reference that identifies the specific occurrence of the
            problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: >-
            A short, human-readable summary of the problem type. This value
            should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
        upstream:
          $ref: '#/components/schemas/Upstream'
          description: >-
            RFC 9457 extension member: the error a proxied third-party provider
            reported. Absent unless the emitting service explicitly surfaced
            one.
      type: object
    PayeeBody:
      additionalProperties: false
      properties:
        accountDigit:
          description: Recipient account check digit.
          examples:
            - '7'
          type: string
        accountNumber:
          description: Recipient account number.
          examples:
            - '123456'
          type: string
        accountType:
          description: >-
            Recipient JDPI tpConta (0=Conta Corrente, 1=Conta Salario, 2=Conta
            Poupanca, 3=Conta de Pagamento, 4=Conta PI). REQUIRED and never
            defaulted: JDPI section 8.4.1 marks recebedor.tpConta mandatory and
            its domain has no 'unknown' member, so sending 0 for an account you
            have not identified asserts 'Conta Corrente' about a savings or
            payment account and settles the money against a false type. Take it
            from decode-QRCode (section 8.3.3) or consultar-chave (section
            8.2.14), which both return it, or from the recipient details on a
            manually-typed order.
          examples:
            - 0
          format: int64
          type: integer
        branch:
          description: Recipient agency/branch.
          examples:
            - '0001'
          type: string
        document:
          description: Recipient document (CPF/CNPJ).
          examples:
            - '12345678909'
          type: string
        ispb:
          description: Recipient participant ISPB (equal to the plugin ISPB routes on-us).
          examples:
            - '12345678'
          type: string
        key:
          description: Recipient PIX key, when the payment targets a key.
          examples:
            - foo@bar.com
          type: string
        name:
          description: Recipient holder name.
          examples:
            - Jane Doe
          type: string
      required:
        - ispb
        - document
        - accountNumber
        - accountDigit
        - accountType
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: >-
            Where the error occurred, e.g. 'body.items[3].tags' or
            'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
    Upstream:
      additionalProperties: false
      properties:
        code:
          description: The upstream provider's own error code, verbatim.
          examples:
            - E4001
          type: string
        message:
          description: >-
            The upstream provider's own error message, verbatim (bounded, never
            its raw response body).
          examples:
            - account not found at provider
          type: string
      type: object
  securitySchemes:
    BearerAuth:
      bearerFormat: JWT
      description: JWT bearer token issued by the identity provider.
      scheme: bearer
      type: http

````