> ## 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.

# Process a payee-bank-initiated PIX devolução

> Registers a PIX devolução that the PAYEE's bank initiated and JDPI has already executed (JDPI section 9.3.3). This participant does not initiate it and does not decide it: JDPI pushes it once the return has settled, and this call records the money coming back. It is idempotent on endToEndIdDevolucao, so a redelivery answers 409 PIX-0029 and credits nothing twice.

On success the returned amount is credited back to the account that funded the ORIGINAL payment, and the response carries the section 9.3.3 acknowledgement: idReqJdPi, idCreditoSgct (the identifier of the entry this participant recorded) and dtHrCreditoSgct. The original payment is located by endToEndIdOriginal; the payer's identity and destination are read from the record written when that payment was created, never re-resolved, so a devolução always returns to where the money left from.

ONE SUCCESS SHAPE IS NOT A COMPLETED CREDIT, and it is the case an integrator must plan for. When the original payment is still PENDING here (its SPI outcome has not been reconciled yet), the devolução is ACCEPTED AND OWNED rather than credited: it is recorded as awaiting the original's settlement, the acknowledgement is the same 200 shape with idCreditoSgct naming that record, and a background sweep completes the credit once the original settles. So a 200 asserts this participant has taken responsibility for the devolução, not that the money has already moved.

WHEN THE ORIGINAL PAYMENT BELONGED TO AN INDIRECT PARTICIPANT, the devolução follows it. This participant may serve indirect participants — other institutions registered here with POST /v1/indirects that reach SPI through this participant's ISPB. A payment originated for one of them was debited from its `@pi_{ispb}` settlement position, so the return credits that same position and the recorded devolução is stamped with the same institution as the original. Nothing in the section 9.3.3 payload says so — the linkage comes entirely from the original payment's record — and the 200 body is identical either way.

An indirect-linked devolução also queues a NOTICE to that registration's delivery.endpointUrl: one HTTPS POST forwarding this webhook's JD 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 independent of this 200: it can repeat, and it can fail permanently while the money stays booked. The institution must dedup on endToEndIdDevolucao and read GET /v1/indirects/{indirectId}/transactions as the record of truth.

A devolução that CANNOT be registered is answered with an HTTP error carrying a distinct code in the problem body's `code` member:

- 404 PIX-0063 - no original payment with this endToEndIdOriginal exists here. This participant never made that payment, so there is nothing to return.
- 409 PIX-0029 - this endToEndIdDevolucao was already registered. The redelivery credited nothing; the original devolução stands.
- 409 PIX-0032 - the returned amounts for this payment would exceed what was originally paid. The cap is cumulative across every devolução already booked against that payment, and it is evaluated under a lock, so two devoluções arriving at once cannot both pass it.
- 422 PIX-0030 - the devolução amount is not greater than zero.
- 400 PIX-0061 - the body failed schema validation at the edge, before any lookup ran.

None of those are specific to an indirect participant: an indirect-linked devolução is refused for exactly the same reasons as a direct one, because the linkage is read from the original payment rather than resolved from this request.



## OpenAPI

````yaml /en/openapi/v3-current/pix.yaml post /v1/webhooks/refunds
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/webhooks/refunds:
    post:
      tags:
        - Webhooks
      summary: Process a payee-bank-initiated PIX devolução
      description: >-
        Registers a PIX devolução that the PAYEE's bank initiated and JDPI has
        already executed (JDPI section 9.3.3). This participant does not
        initiate it and does not decide it: JDPI pushes it once the return has
        settled, and this call records the money coming back. It is idempotent
        on endToEndIdDevolucao, so a redelivery answers 409 PIX-0029 and credits
        nothing twice.


        On success the returned amount is credited back to the account that
        funded the ORIGINAL payment, and the response carries the section 9.3.3
        acknowledgement: idReqJdPi, idCreditoSgct (the identifier of the entry
        this participant recorded) and dtHrCreditoSgct. The original payment is
        located by endToEndIdOriginal; the payer's identity and destination are
        read from the record written when that payment was created, never
        re-resolved, so a devolução always returns to where the money left from.


        ONE SUCCESS SHAPE IS NOT A COMPLETED CREDIT, and it is the case an
        integrator must plan for. When the original payment is still PENDING
        here (its SPI outcome has not been reconciled yet), the devolução is
        ACCEPTED AND OWNED rather than credited: it is recorded as awaiting the
        original's settlement, the acknowledgement is the same 200 shape with
        idCreditoSgct naming that record, and a background sweep completes the
        credit once the original settles. So a 200 asserts this participant has
        taken responsibility for the devolução, not that the money has already
        moved.


        WHEN THE ORIGINAL PAYMENT BELONGED TO AN INDIRECT PARTICIPANT, the
        devolução follows it. This participant may serve indirect participants —
        other institutions registered here with POST /v1/indirects that reach
        SPI through this participant's ISPB. A payment originated for one of
        them was debited from its `@pi_{ispb}` settlement position, so the
        return credits that same position and the recorded devolução is stamped
        with the same institution as the original. Nothing in the section 9.3.3
        payload says so — the linkage comes entirely from the original payment's
        record — and the 200 body is identical either way.


        An indirect-linked devolução also queues a NOTICE to that registration's
        delivery.endpointUrl: one HTTPS POST forwarding this webhook's JD 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
        independent of this 200: it can repeat, and it can fail permanently
        while the money stays booked. The institution must dedup on
        endToEndIdDevolucao and read GET /v1/indirects/{indirectId}/transactions
        as the record of truth.


        A devolução that CANNOT be registered is answered with an HTTP error
        carrying a distinct code in the problem body's `code` member:


        - 404 PIX-0063 - no original payment with this endToEndIdOriginal exists
        here. This participant never made that payment, so there is nothing to
        return.

        - 409 PIX-0029 - this endToEndIdDevolucao was already registered. The
        redelivery credited nothing; the original devolução stands.

        - 409 PIX-0032 - the returned amounts for this payment would exceed what
        was originally paid. The cap is cumulative across every devolução
        already booked against that payment, and it is evaluated under a lock,
        so two devoluções arriving at once cannot both pass it.

        - 422 PIX-0030 - the devolução amount is not greater than zero.

        - 400 PIX-0061 - the body failed schema validation at the edge, before
        any lookup ran.


        None of those are specific to an indirect participant: an
        indirect-linked devolução is refused for exactly the same reasons as a
        direct one, because the linkage is read from the original payment rather
        than resolved from this request.
      operationId: createWebhookReversal
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalRefundBody'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalRefundResponse'
          description: OK
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Bad Request
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Not Found
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Conflict
        '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
components:
  schemas:
    ExternalRefundBody:
      additionalProperties: true
      properties:
        codigoDevolucao:
          description: Return reason code (eRefundCode).
          examples:
            - MD06
          type: string
        endToEndIdDevolucao:
          description: JDPI-assigned return id (idempotency + dedup key).
          examples:
            - E2E-DEVO
          type: string
        endToEndIdOriginal:
          description: End-to-end id of the original CASH_OUT.
          examples:
            - E2E-ORIG
          type: string
        idReqJdPi:
          description: JDPI request id (GUID), echoed on the acknowledgement.
          examples:
            - JDPI-1
          type: string
        infEntreClientes:
          description: Free-text inter-client info.
          examples:
            - note
          type: string
        motivoDevolucao:
          description: Free-text return reason.
          examples:
            - customer request
          type: string
        valor:
          description: Return amount in reais (a JSON number).
          examples:
            - 50.25
          format: double
          type: number
      required:
        - idReqJdPi
        - endToEndIdOriginal
        - endToEndIdDevolucao
        - valor
      type: object
    ExternalRefundResponse:
      additionalProperties: false
      properties:
        dtHrCreditoSgct:
          description: SGCT credit timestamp (ISO-8601).
          examples:
            - '2026-06-20T10:00:00Z'
          type: string
        dtHrValidacao:
          description: Validation timestamp (ISO-8601).
          examples:
            - '2026-06-20T10:00:00Z'
          type: string
        idCreditoSgct:
          description: SGCT credit id.
          examples:
            - SGCT-1
          type: string
        idReqJdPi:
          description: Echoed JDPI request id.
          examples:
            - JDPI-1
          type: string
        motivo:
          description: Reason code (on a rejected return).
          examples:
            - BE01
          type: string
        motivoComplemento:
          description: Reason complement.
          examples:
            - ...
          type: string
        resultado:
          description: Validation result (0=Inválido, 1=Válido).
          examples:
            - 1
          format: int64
          type: integer
      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
    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

````