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

# Return a Transfer

> Use this endpoint to return one completed transfer to the party that paid it. The return is a new transfer that names the original in `originalTransferId`. What happens depends on the type of the original.

**TED IN.** The original must be a `COMPLETED` `TED_IN` received as STR0008R2. The plugin places a hold of the credited amount on the recipient's account. It then sends a voluntary STR0010, return reason code `70`, which returns the gross amount of the TED IN to the paying bank. The return is a `TED_OUT` with no fee, and the answer is `PROCESSING`. The final outcome arrives later. When the STR settles the return, the plugin reverts the original credit in the ledger. When nothing proves whether the STR0010 left, the answer carries `reconciliationStatus` `AWAITING`, and reconciliation decides. A customer credit with a `TED_IN_STR` type cannot be returned.

A TED IN return runs only inside the operating window of the tenant. On 24 December and on the last business day of the year, the window for returns closes no later than 13:00. The return posts on the `TED_OUT` ledger routing of the ledger of the TED IN. It cannot be cancelled or sent back to the reconciliation queue by hand.

**P2P.** The original must be a `COMPLETED` `P2P` that is not a return itself. The ledger reverses the original transaction in this request: the return debits the account that received the original and credits the account that paid it. The answer is `COMPLETED`, and the original stays `COMPLETED`. A P2P return has no operating window, no hold, and no ledger routing. On a P2P inside one ledger, the plugin first checks the balance of the account that received the original. If the outcome in the ledger is unknown, the answer is `503 BTF-2000`, and `transferId` names the return, still `CREATED`. Send the same request again under a new `X-Idempotency` key: it resumes that return and never creates a second one.

A return needs Midaz 4.1.3 or later, and 4.2.0 or later for a P2P between two ledgers. A transfer has one live return at most. The body is optional and accepts only `externalReference`. A request with an application token must send it.

This endpoint requires the `transfers:return` permission.



## OpenAPI

````yaml /es/openapi/v3-current/ted.yaml post /v1/transfers/{transferId}/return
openapi: 3.0.3
info:
  title: Bank Transfer (TED) Plugin API
  description: >-
    Complete API for Brazilian bank transfers (TED OUT, TED IN, P2P) through the
    Lerian platform, including transfer initiation, processing, status tracking,
    cancellation, and returns.
  version: 3.3.0
servers:
  - url: https://api.example.com
    description: >-
      Replace the example host with the URL provided during environment
      onboarding.
security:
  - BearerAuth: []
  - OAuth2ClientCredentials:
      - api
  - OAuth2Password:
      - api
tags:
  - name: Transfers API
    description: >-
      Endpoints for initiating, processing, tracking, cancelling, and returning
      transfers.
  - name: Webhooks
    description: >-
      Endpoints for registering and managing tenant-scoped webhook subscriptions
      for transfer events.
  - name: Webhook DLQ
    description: Endpoints for inspecting and retrying webhook dead-letter queue messages.
paths:
  /v1/transfers/{transferId}/return:
    post:
      tags:
        - Transfers API
      summary: Return a Transfer
      description: >-
        Use this endpoint to return one completed transfer to the party that
        paid it. The return is a new transfer that names the original in
        `originalTransferId`. What happens depends on the type of the original.


        **TED IN.** The original must be a `COMPLETED` `TED_IN` received as
        STR0008R2. The plugin places a hold of the credited amount on the
        recipient's account. It then sends a voluntary STR0010, return reason
        code `70`, which returns the gross amount of the TED IN to the paying
        bank. The return is a `TED_OUT` with no fee, and the answer is
        `PROCESSING`. The final outcome arrives later. When the STR settles the
        return, the plugin reverts the original credit in the ledger. When
        nothing proves whether the STR0010 left, the answer carries
        `reconciliationStatus` `AWAITING`, and reconciliation decides. A
        customer credit with a `TED_IN_STR` type cannot be returned.


        A TED IN return runs only inside the operating window of the tenant. On
        24 December and on the last business day of the year, the window for
        returns closes no later than 13:00. The return posts on the `TED_OUT`
        ledger routing of the ledger of the TED IN. It cannot be cancelled or
        sent back to the reconciliation queue by hand.


        **P2P.** The original must be a `COMPLETED` `P2P` that is not a return
        itself. The ledger reverses the original transaction in this request:
        the return debits the account that received the original and credits the
        account that paid it. The answer is `COMPLETED`, and the original stays
        `COMPLETED`. A P2P return has no operating window, no hold, and no
        ledger routing. On a P2P inside one ledger, the plugin first checks the
        balance of the account that received the original. If the outcome in the
        ledger is unknown, the answer is `503 BTF-2000`, and `transferId` names
        the return, still `CREATED`. Send the same request again under a new
        `X-Idempotency` key: it resumes that return and never creates a second
        one.


        A return needs Midaz 4.1.3 or later, and 4.2.0 or later for a P2P
        between two ledgers. A transfer has one live return at most. The body is
        optional and accepts only `externalReference`. A request with an
        application token must send it.


        This endpoint requires the `transfers:return` permission.
      operationId: returnTransfer
      parameters:
        - $ref: '#/components/parameters/XOrganizationId'
        - $ref: '#/components/parameters/XIdempotencyKey'
        - name: transferId
          in: path
          required: true
          description: The unique identifier of the TED IN or P2P to return.
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        description: >-
          The reference of your system for this return. Required when the token
          belongs to an application.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReturnTransferRequest'
      responses:
        '201':
          description: >-
            Indicates that the return was created. The body depends on `type`:
            `TED_OUT` for the return of a TED IN, `P2P` for the return of a P2P.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ReturnIncomingTransferResponse'
                  - $ref: '#/components/schemas/ReturnInternalTransferResponse'
        '400':
          description: >-
            BTF-0001: `transferId` is not a UUID, the `X-Idempotency` key is
            missing or invalid, or the body is not one JSON document, carries a
            member other than `externalReference`, or carries an
            `externalReference` outside `^[A-Za-z0-9._:/-]{1,128}$`. A request
            with an application token that carries no `externalReference` gets
            the same answer. Nothing was created.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDocument'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/TransferNotFound'
        '409':
          description: >-
            BTF-0119: the transfer already has a live return. `transferId` names
            that return, and `originalTransferId` names the transfer that you
            asked to return. A TED IN accepts a new return only after the
            earlier one ends `FAILED`, `CANCELLED`, or `REJECTED` with no
            clearing control number and no reversal recorded. A P2P accepts a
            new return after the earlier one ends `FAILED`. A P2P return that is
            still `CREATED` is not refused: the request resumes it.


            BTF-0016: a request under this `X-Idempotency` key still holds the
            in-flight lease, because it is still running or because it already
            ended 5xx and recorded no outcome. `Retry-After` carries a poll
            interval, not the lease. While the first request is still running,
            resend the identical request after that interval; the answer replays
            from the first request as soon as it finishes. If an earlier request
            under this key answered 5xx without `X-Idempotency-Fenced: true`, no
            answer will ever be served from it: stop resending, read the
            resource this route acts on, and send the work under a new
            `X-Idempotency` key only if it did not land. The detail also names
            the 15-minute bound after which the lease lapses and a resend runs
            the work a second time; a poll still unanswered by then ends the
            same way.


            BTF-0018: the request under this `X-Idempotency` key already
            completed, and its response was too large to store for replay. Read
            the resource. Do not resend under a new key, because that runs the
            work a second time. This answer carries no `Retry-After`, because no
            wait produces the missing receipt.
          headers:
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDocument'
        '422':
          description: >-
            BTF-0118: the transfer cannot be returned. It is neither a
            `COMPLETED` TED IN received as STR0008R2 that carries the original
            clearing data, nor a `COMPLETED` P2P that is not a return itself.
            Nothing was created.


            BTF-0120: the ledger cannot reverse the original transaction.
            `reason` is `route_not_reversible`, `original_not_approved`,
            `fee_account_insufficient`, or `ledger_platform_below_minimum`, and
            nothing was reserved or sent. On a P2P return, `reason` can also be
            `ledger_refused`: the ledger refused the reversal with the code in
            `upstream.code`, and `transferId` names the return, now `FAILED`.


            BTF-0121: the ledger refused the hold on the account that received
            the original. `reason` is `insufficient_balance` or
            `prior_reservation_pending`, and `transferId` names the return, now
            `FAILED`. On a P2P inside one ledger, the plugin refuses
            `insufficient_balance` before it creates anything.


            BTF-0006: on a P2P return between two ledgers, the ledger refused to
            reverse the group. `reason` is `ledger_not_enabled`,
            `bridge_route_missing`, or `bridge_route_invalid`, with the ledger
            code in `upstream.code`. BTF-0007: on the same return, the assets do
            not match (`reason` is `asset_mismatch`). BTF-2004: on a P2P return,
            the Tracer of the ledger denied the reversal or sent it to review.
            In these three cases, `transferId` names the return, now `FAILED`,
            and nothing moved. Request the return again under a new
            `X-Idempotency` key once the cause is fixed.


            BTF-0010: the clearing house is closed for returns, or the holiday
            calendar could not be read (`classification` is
            `holiday_lookup_dependency_unavailable`). Nothing was created.


            BTF-1001: JD SPB refused the STR0010. `upstream.code` carries the JD
            code, and `transferId` names the return.


            BTF-1007: JD SPB reports that the password of the plugin expired
            (`upstream.code` is `AAE08`). `transferId` names the return.


            BTF-0014: an earlier request under this `X-Idempotency` key answered
            5xx, so the key is fenced for the retention window. Read the
            resource and, only if the work did not land, send the identical
            request under a new key.


            BTF-0015: this service cannot read the idempotency record under this
            key. The remedy is the same.


            BTF-0017: this `X-Idempotency` key was already used for a different
            request. A repeat must be byte-identical, and a new request needs
            its own new key. BTF-0017 is decided before BTF-0014, so a fenced
            key resent with a changed body gets BTF-0017.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDocument'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          description: >-
            BTF-9000: an unexpected internal error occurred. Read the original
            transfer before you send anything else. On a P2P return,
            `transferId` can name the return, still `CREATED`.
          headers:
            X-Idempotency-Fenced:
              $ref: '#/components/headers/XIdempotencyFenced'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDocument'
        '502':
          description: >-
            BTF-1007: JD SPB rejected the credentials of the plugin. BTF-1008:
            JD SPB rejected the signing certificate or the digital signature.
            BTF-1009: JD SPB rejected the configuration. Each one carries the JD
            code in `upstream.code` and names the return in `transferId`.
          headers:
            X-Idempotency-Fenced:
              $ref: '#/components/headers/XIdempotencyFenced'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDocument'
        '503':
          description: >-
            BTF-0124: the plugin cannot make sure now that the transfer has one
            live return at most. Nothing was created. When the cause clears,
            request the return again under a new `X-Idempotency` key.


            BTF-2000: the ledger did not answer. `transferId` names the return
            when it exists. A TED IN return is then closed by the plugin. A P2P
            return stays `CREATED`, and its outcome in the ledger is unknown:
            send the same request again under a new `X-Idempotency` key. It
            resumes that return and never creates a second one. When the answer
            carries `Retry-After`, wait that many seconds first.


            BTF-1000: JD SPB was unavailable, and nothing left the plugin.
            `transferId` names the return.


            BTF-9015: the ledger routing of this return could not be resolved.
            Nothing was created.


            BTF-9000: the idempotency store was unavailable before the handler
            ran, so nothing ran. Send the same request again under the same
            `X-Idempotency` key.


            BTF-0019: the work under this `X-Idempotency` key ran, and neither
            its response nor its outcome fence could be stored, so the key
            protects nothing and a resend can run the operation a second time.
            Read the resource before you send anything else.


            BTF-0020: the work under this `X-Idempotency` key ran and its
            response could not be stored for replay, but the key is still held.
            This answer is not the outcome of that work. Read the resource, and
            resend under a new key only if the work did not land.


            BTF-9017: the tenant organization list is absent or unreadable.
            Retry with the same idempotency key once it is configured.
          headers:
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
            X-Idempotency-Fenced:
              $ref: '#/components/headers/XIdempotencyFenced'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDocument'
components:
  parameters:
    XOrganizationId:
      name: X-Organization-Id
      in: header
      required: true
      description: >-
        Midaz organization scope for the request, used for downstream CRM, Fees,
        and Midaz calls. Required on org-scoped transfer routes in every
        deployment mode; a missing or non-UUID value returns 400. This is not
        the tenant identifier — tenantId is derived from the bearer JWT or
        authenticated context, never from this header. Background workers (TED
        IN poller, reconciliation) have no request header and, in single-tenant
        mode, fall back to the deployment's `ORGANIZATION_ID` env.
      schema:
        type: string
        format: uuid
      example: 019c96a0-0a98-7287-9a31-786e0809c769
    XIdempotencyKey:
      name: X-Idempotency
      in: header
      required: true
      description: >-
        Required idempotency key for safe retries. Use a UUID v4 or a unique
        business identifier, made of letters, digits, `.`, `_`, and `-`. If the
        same key is sent again and the original request was already processed,
        the cached response is returned.


        See [Retries and idempotency](/es/reference/retries-idempotency) for
        details.
      schema:
        type: string
        maxLength: 255
        pattern: ^[a-zA-Z0-9._-]+$
      example: 019c96a0-aa10-7abc-d1e2-8c9d0e1f2a3b
  schemas:
    ReturnTransferRequest:
      type: object
      additionalProperties: false
      properties:
        externalReference:
          type: string
          maxLength: 128
          pattern: ^[A-Za-z0-9._:/-]{1,128}$
          description: >-
            The reference of your system for this return. The plugin records it
            on the return and in its audit, and sends it back in the answer.
            Required when the token belongs to an application. Do not put
            personal data in it.
          example: crm-ticket-88231
    ReturnIncomingTransferResponse:
      type: object
      description: The return of a TED IN.
      properties:
        transferId:
          type: string
          format: uuid
          description: The return, a new `TED_OUT` transfer.
          example: 9b2f1c7e-4d3a-4f1e-9c2b-7a6d5e4f3a21
        originalTransferId:
          type: string
          format: uuid
          description: The TED IN that this return sends back.
          example: 660e8400-e29b-41d4-a716-446655440002
        type:
          type: string
          enum:
            - TED_OUT
          example: TED_OUT
        status:
          type: string
          enum:
            - PROCESSING
          description: The final status arrives later.
          example: PROCESSING
        reconciliationStatus:
          type: string
          enum:
            - AWAITING
          description: >-
            Present when nothing proves whether the STR0010 left. Reconciliation
            then decides.
          example: AWAITING
        amount:
          type: number
          format: decimal
          description: >-
            The gross amount of the TED IN, sent in the STR0010. It is not the
            net amount credited to the recipient.
          example: 1500
        returnReasonCode:
          type: string
          description: The return reason code sent in the STR0010. Always `70`.
          example: '70'
        controlNumber:
          type: string
          description: The control number (NumCtrlIF) of the STR0010 sent.
          example: 20261014a1b2c3d4e5f6
        originalClearingControlNumber:
          type: string
          description: The STR control number (NumCtrlSTR) of the original STR0008.
          example: STR20261013033030760
        requestedVia:
          type: string
          enum:
            - user
            - application
            - unknown
          description: The channel that the token names.
          example: application
        externalReference:
          type: string
          description: The `externalReference` of the request, when one was sent.
          example: crm-ticket-88231
        createdAt:
          type: string
          format: date-time
          example: '2026-10-14T15:30:00Z'
    ReturnInternalTransferResponse:
      type: object
      description: The return of a P2P.
      properties:
        transferId:
          type: string
          format: uuid
          description: The return, a new `P2P` transfer.
          example: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
        originalTransferId:
          type: string
          format: uuid
          description: The P2P that this return reverses. It stays `COMPLETED`.
          example: 770e8400-e29b-41d4-a716-446655440003
        type:
          type: string
          enum:
            - P2P
          example: P2P
        status:
          type: string
          enum:
            - COMPLETED
          example: COMPLETED
        amount:
          type: number
          format: decimal
          description: The amount of the original P2P.
          example: 250
        ledgerId:
          type: string
          format: uuid
          description: >-
            The ledger of the account that the return debits, which received the
            original.
          example: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
        recipientLedgerId:
          type: string
          format: uuid
          nullable: true
          description: >-
            The ledger of the account that the return credits, which paid the
            original. `null` on a P2P inside one ledger.
          example: b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e
        reversalTransactionId:
          type: string
          format: uuid
          description: >-
            The ledger transaction that reverses the original transaction. It is
            on the ledger of the account that paid the original.
          example: 7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f
        requestedVia:
          type: string
          enum:
            - user
            - application
            - unknown
          description: The channel that the token names.
          example: user
        externalReference:
          type: string
          description: >-
            The `externalReference` of the request that created the return, when
            one was sent.
          example: crm-ticket-88231
        createdAt:
          type: string
          format: date-time
          example: '2026-10-22T14:00:00Z'
        completedAt:
          type: string
          format: date-time
          example: '2026-10-22T14:00:05Z'
    ProblemDocument:
      type: object
      description: >-
        The RFC 9457 problem document that the service answers every error with,
        on `application/problem+json`. Route-specific members (for example
        `refusalCode`, `transactionId`, `ledgerStatus`, `systemplaneKey`) appear
        flat at the top level beside these.
      additionalProperties: true
      required:
        - type
        - title
        - status
        - detail
        - code
        - requestId
        - service
        - category
      properties:
        type:
          type: string
          description: >-
            A URI that names the error code:
            `https://errors.lerian.studio/v1/<code>`.
          example: https://errors.lerian.studio/v1/BTF-0200
        title:
          type: string
          description: The HTTP status text.
          example: Not Found
        status:
          type: integer
          description: The HTTP status code.
          example: 404
        detail:
          type: string
          description: A human-readable explanation of this occurrence.
          example: transfer not found
        instance:
          type: string
          description: The trace ID of this request, when one was recorded.
          example: 4bf92f3577b34da6a3ce929d0e0e4736
        errors:
          type: array
          description: Each rejected field. Present on field validation errors only.
          items:
            type: object
            properties:
              message:
                type: string
                description: Why the value was rejected.
                example: transferId must be a valid UUID
              location:
                type: string
                description: 'Where the value sits: `body.<field>` or `query.<field>`.'
                example: body.senderAccountId
        code:
          type: string
          description: The stable BTF error code.
          example: BTF-0200
        upstream:
          type: object
          description: >-
            The error that an upstream provider (JD SPB, Midaz) reported. Absent
            unless surfaced.
          properties:
            code:
              type: string
              description: The provider's own error code, verbatim.
              example: AAC90
            message:
              type: string
              description: The provider's own error message, bounded.
              example: Assinatura invalida
        requestId:
          type: string
          description: The request correlation ID. Present even when empty.
          example: 6d3e2a68-1f2b-4c3d-9e4f-5a6b7c8d9e0f
        service:
          type: string
          description: The system that produced the error.
          example: plugin
        category:
          type: string
          description: The machine-readable error category.
          example: deterministic
  responses:
    Unauthorized:
      description: Indicates that the request is missing a valid authentication token.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDocument'
          examples:
            unauthorized:
              summary: Missing authorization
              value:
                type: https://errors.lerian.studio/v1/BTF-0401
                title: Unauthorized
                status: 401
                detail: Missing Token
                code: BTF-0401
                category: plugin
                requestId: 6d3e2a68-1f2b-4c3d-9e4f-5a6b7c8d9e0f
                service: plugin
    Forbidden:
      description: >-
        Indicates that the caller is authenticated but lacks permission for this
        operation.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDocument'
          examples:
            permissionDenied:
              summary: Caller lacks permission
              value:
                type: https://errors.lerian.studio/v1/BTF-0405
                title: Forbidden
                status: 403
                detail: >-
                  the credential is not allowed to perform transfers:create on
                  this request
                code: BTF-0405
                category: plugin
                requestId: 6d3e2a68-1f2b-4c3d-9e4f-5a6b7c8d9e0f
                service: plugin
    TransferNotFound:
      description: >-
        BTF-0200: the transfer was not found or belongs to a different
        organization.


        BTF-0404: `X-Organization-Id` names an organization that this tenant
        does not operate.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDocument'
          examples:
            transferNotFound:
              summary: Transfer ID invalid
              value:
                type: https://errors.lerian.studio/v1/BTF-0200
                title: Not Found
                status: 404
                detail: transfer not found
                code: BTF-0200
                category: deterministic
                requestId: 6d3e2a68-1f2b-4c3d-9e4f-5a6b7c8d9e0f
                service: plugin
                transferId: 019c96a0-ab10-7cde-f1a2-0e1f2a3b4c5d
    RateLimitExceeded:
      description: >-
        Indicates that the rate limit has been exceeded. Retry after the number
        of seconds specified in the Retry-After header.
      headers:
        Retry-After:
          description: Seconds until rate limit resets
          schema:
            type: integer
            example: 60
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDocument'
          examples:
            rateLimitExceeded:
              summary: Too many requests
              value:
                type: https://errors.lerian.studio/v1/BTF-0429
                title: Too Many Requests
                status: 429
                detail: Rate limit exceeded
                code: BTF-0429
                category: rate_limit
                requestId: 6d3e2a68-1f2b-4c3d-9e4f-5a6b7c8d9e0f
                service: plugin
              description: >
                Rate-limit responses use the dedicated `BTF-0429` code with the
                `rate_limit` category. Clients should back off using the HTTP
                status `429` and the `Retry-After` header.
  headers:
    RetryAfter:
      description: The number of seconds to wait before you send the request again.
      schema:
        type: integer
    XIdempotencyFenced:
      description: >-
        `true` when this 5xx fenced the `X-Idempotency` key, so a resend is
        refused with 422 BTF-0014. `false` when the fence write itself failed
        and only the in-flight lease holds the key, so a resend is refused with
        409 BTF-0016 until that lease lapses. On a 503 BTF-0019, `false` means
        the handler succeeded and both its receipt and its fence were lost.


        The header is absent when nothing fenced the key. On a 500 raised by a
        panic, the protection of the key is unknown: read the resource and
        reconcile before you resend. On a 503 BTF-9000 answered while the
        idempotency store was down before the handler, nothing ran: send the
        same request again under the same key.
      schema:
        type: string
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        JWT Bearer token authentication. The tenantId is derived from the bearer
        token or authenticated request context and is not supplied through
        X-Organization-Id.
    OAuth2ClientCredentials:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: /v1/login/oauth/access_token
          scopes:
            api: TED transfer API access
    OAuth2Password:
      type: oauth2
      flows:
        password:
          tokenUrl: /v1/login/oauth/access_token
          scopes:
            api: TED transfer API access

````

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