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

# TED JD events

> CloudEvents emitted by the Bank Transfer plugin for TED OUT, TED IN, P2P, cross-core TED, returns, and reconciliation.

The Bank Transfer plugin emits 21 JSON business facts as CloudEvents 1.0 in binary content mode over Kafka. The same facts feed its [webhooks](https://docs.lerian.studio/en/interfaces/ted-jd/ted-webhooks.md).

## Transport contract

| Field | Value |
| - | - |
| `ce-source` | `plugin-br-bank-transfer` |
| Topic | `lerian.streaming.plugin-br-bank-transfer` |
| `ce-type` | `studio.lerian.plugin-br-bank-transfer.<event-key>` |
| Schema version | Per event, in the catalog below |
| Content type | `application/json` |
| `ce-subject` | The transfer id. `payment_initiation.created` carries the initiation id, and `transfer_incoming.undeliverable` carries its `jdIncomingMessageId` |
| `ce-tenantid` | Your tenant, in multi-tenant mode |
| Delivery | Through the PostgreSQL outbox. The outbox relay is the only broker publisher |
| Runtime manifest | `GET /streaming/manifest` |

Set `STREAMING_ENABLED=true` and the [streaming variables](https://docs.lerian.studio/en/interfaces/ted-jd/ted-environment-variables.md#streaming-outbox). While streaming is off, the plugin discards events. If you set `STREAMING_CLOUDEVENTS_SOURCE`, its value must be exactly `plugin-br-bank-transfer`. In production, the plugin serves the manifest only when authentication is on.

Some events are best effort: when their outbox write fails, the plugin keeps the change that they announce and does not publish the event. If an expected event does not arrive, read the transfer with [Get Transfer](https://docs.lerian.studio/en/reference/interfaces/ted-jd/retrieve-transfer.md).

## Event catalog

### Initiation and transfer lifecycle

| Event key | Schema | Fires when |
| - | - | - |
| `payment_initiation.created` | `1.0.0` | [Initiate Transfer](https://docs.lerian.studio/en/reference/interfaces/ted-jd/initiate-transfer.md) created an initiation, before its confirmation. |
| `transfer.initiated` | `1.0.0` | The plugin created a TED OUT or [cross-core TED](https://docs.lerian.studio/en/interfaces/ted-jd/ted-cross-core.md) transfer from a confirmed initiation. |
| `transfer.processing_started` | `1.0.0` | A TED OUT entered processing (`CREATED` to `PENDING` to `PROCESSING`). |
| `transfer.completed` | `1.2.0` | A TED OUT, P2P, or cross-core TED completed. For a TED OUT, the STR settled it, or an operator settled it by hand. For a cross-core TED, the other core credited the recipient. |
| `transfer.rejected` | `1.1.0` | A TED OUT or cross-core TED was rejected, and its hold was cancelled. For a TED OUT, JD refused it at dispatch, or the STR refused the settlement. For a cross-core TED, the other core refused the credit, or the JD Courier reached no recipient. |

### Reconciliation

| Event key | Schema | Fires when |
| - | - | - |
| `transfer.reconciliation_required` | `1.0.0` | A TED OUT with an unknown outcome moved to reconciliation. |
| `transfer.reconciliation_resolved` | `1.5.0` | The reconciliation worker or an operator resolved a transfer. |
| `transfer.reconciliation_exhausted` | `1.1.0` | The reconciliation worker stopped after the maximum number of attempts and moved the transfer to manual review. |
| `transfer.reconciliation_failed` | `1.0.0` | The reconciliation worker failed a TED OUT on a deterministic error. |
| `transfer.reconciliation_manual_retry_requested` | `1.0.0` | An operator returned a TED OUT from manual review to the reconciliation queue. |
| `transfer.scope_repaired` | `1.0.0` | An operator corrected the ledger that a transfer is filed under. No money moved. |

### Incoming transfers

| Event key | Schema | Fires when |
| - | - | - |
| `transfer_incoming.completed` | `1.0.0` | The plugin credited a TED IN or a [customer credit](https://docs.lerian.studio/en/interfaces/ted-jd/ted-in.md#customer-credits), or booked a cross-core credit or return from the other core (`transferType` `CROSS_CORE_IN`). |
| `transfer_incoming.chargeback` | `1.1.0` | A completed TED IN failed on an STR0010R2 chargeback. The TED IN worker sends it, and so does an operator who closes a parked chargeback by hand. |
| `transfer_incoming.undeliverable` | `1.0.0` | The plugin recorded an incoming credit that it could not deliver. |
| `transfer_incoming.return_requested` | `1.0.0` | The [return of a TED IN](https://docs.lerian.studio/en/interfaces/ted-jd/ted-in.md#return-a-received-transfer) entered processing: the hold is placed, and the STR0010 is about to be sent. |
| `transfer_incoming.returned` | `1.0.0` | The STR settled the return of a TED IN, and the original credit was reverted. |
| `transfer_incoming.return_rejected` | `1.0.0` | The return of a TED IN was refused, and its hold was cancelled. |
| `transfer_incoming.return_manual_review` | `1.0.0` | The return of a TED IN waits for an operator. |

### Devolutions and P2P returns

| Event key | Schema | Fires when |
| - | - | - |
| `transfer_outgoing.devolution_notified` | `1.1.0` | The plugin recorded a devolution (STR0010R2) of a TED OUT on the original transfer. |
| `transfer_internal.returned` | `1.0.0` | The return of a P2P completed, and the ledger reverted the original transaction. |
| `transfer_internal.return_failed` | `1.0.0` | The ledger refused the return of a P2P. Nothing moved, and the P2P can be returned again. |

## Deduplication

Deduplicate on `(ce-source, ce-id)`, and keep consumers idempotent. Two events also need a business key:

* **`transfer_incoming.chargeback`**: the TED IN worker and an operator can each announce one chargeback, under two ids. Deduplicate on `tenantId`, `transferId`, and `status`.
* **`transfer.reconciliation_resolved`**: an operator resolution can arrive twice. `convergedOntoExistingResolution` marks the copy that found the resolution already written. Deduplicate on `tenantId`, `transferId`, `status`, and `reconciliationStatus`, not on that marker.

## Payload contracts

Amounts are decimal strings, for example `"1500.00"`. Timestamps are UTC RFC 3339 strings. Fields marked with `?` are optional. `tenantId` is empty in single-tenant mode.

```typescript theme={null}
// payment_initiation.created
interface PaymentInitiationCreated {
  paymentInitiationId: string
  tenantId: string
  organizationId?: string
  ledgerId: string
  senderAccountId: string
  recipientAccountId?: string
  recipientIspb: string
  amount: string
  feeAmount: string
  totalAmount: string
  transferType: string
  status: string
  purpose?: string
  expiresAt: string
  createdAt: string
}

// transfer.initiated
interface TransferInitiated {
  transferId: string
  paymentInitiationId: string
  tenantId: string
  organizationId?: string
  ledgerId: string
  senderAccountId: string
  recipientAccountId?: string
  amount: string
  feeAmount: string
  totalAmount: string
  transferType: string
  status: string
  confirmationNumber: string
  controlNumber: string
  midazTransactionId?: string
  signingArtifactId?: string
  createdAt: string
}

// transfer.processing_started
interface TransferProcessingStarted {
  transferId: string
  tenantId: string
  transferType: string
  status: string
  controlNumber: string
  statusPath: string
  processingStartedAt: string
}

// transfer.completed
interface TransferCompleted {
  transferId: string
  initiationId: string
  tenantId: string
  organizationId?: string
  ledgerId: string // on a P2P between ledgers, the payer's ledger
  senderAccountId: string
  recipientAccountId?: string
  midazTransactionId: string
  confirmationNumber: string
  status: string
  transferType: string // TED_OUT, P2P, or CROSS_CORE
  amount: string
  feeAmount: string
  totalAmount: string
  packageAppliedId?: string
  completedAt: string
  // TED OUT only
  source?: "str0008r1" | "reconciliation" | "manual"
  controlNumber?: string
  clearingControlNumber?: string
  messageSequence?: string
  settledAt?: string
  // P2P between ledgers only
  recipientLedgerId?: string
  ledgerGroupId?: string
}

// transfer.rejected
interface TransferRejected {
  transferId: string
  tenantId: string
  transferType: string // TED_OUT or CROSS_CORE
  status: string
  failureCode: string // BTF-4005 on a cross-core TED
  failureMessage: string
  errorCategory: string
  midazCancelled: boolean
  midazTransactionId?: string
  rejectedAt: string
  // TED OUT only
  source?: "dispatch" | "str0008r1" | "reconciliation"
  rejectedBy?: "jd" | "bacen"
  settlementRejectionCode?: string
  controlNumber?: string
  clearingControlNumber?: string
  messageSequence?: string
}

// transfer.reconciliation_required
interface TransferReconciliationRequired {
  transferId: string
  tenantId: string
  transferType: string
  status: string
  reconciliationStatus: string
  controlNumber?: string
  counterpartySystem: string
  failureClass: string
  failureCode: string
  errorCategory: string
  reason: string
  reconciliationEnqueuedAt: string
}

// transfer.reconciliation_resolved
interface TransferReconciliationResolved {
  transferId: string
  tenantId: string
  status: string
  transferType: string
  reconciliationStatus: string
  resolution: string
  reasonCategory: string
  attempts: number
  jdResponseCode?: string
  controlNumber?: string
  jdSequenceNumber?: string
  clearingControlNumber?: string
  // operator resolution only
  actor?: string
  operatorReason?: string
  evidenceReference?: string
  convergedOntoExistingResolution?: boolean
  resolutionAuthorizedBy?: string
  // resolution manual_repost only
  previousCompensationTransactionId?: string
  compensationTransactionId?: string
}

// transfer.reconciliation_exhausted
interface TransferReconciliationExhausted {
  transferId: string
  tenantId: string
  status: string
  transferType: string
  reconciliationStatus: string
  resolution: string
  reasonCategory: string
  attempts: number
  maxAttempts: number
  holdPreserved: boolean
  jdResponseCode?: string
  controlNumber?: string
  jdSequenceNumber?: string
}

// transfer.reconciliation_failed
interface TransferReconciliationFailed {
  transferId: string
  tenantId: string
  status: string
  reconciliationStatus: string
  resolution: string
  reasonCategory: string
  attempts: number
  errorCategory: string
  jdResponseCode?: string
  controlNumber?: string
  jdSequenceNumber?: string
}

// transfer.reconciliation_manual_retry_requested
interface TransferReconciliationManualRetryRequested {
  transferId: string
  tenantId: string
  status: string
  transferType: string
  reconciliationStatus: string
  operatorReason: string
  previousAttempts: number
  requestId?: string
  requeuedAt: string
}

// transfer.scope_repaired
interface TransferScopeRepaired {
  transferId: string
  tenantId: string
  ledgerId: string
  status: string
  transferType: string
  actor: string
  operatorReason: string
  evidenceReference?: string
  requestId?: string
  repairedAt: string
}

// transfer_incoming.completed
interface TransferIncomingCompleted {
  transferId: string
  tenantId: string
  status: string
  transferType: string // TED_IN, TED_IN_STR0005 to TED_IN_STR0051, or CROSS_CORE_IN
  amount: string
  feeAmount: string
  grossAmount: string
  controlNumber: string // empty on CROSS_CORE_IN
  messageSequence: string // empty on CROSS_CORE_IN
  repairedBy: string
}

// transfer_incoming.chargeback
interface TransferIncomingChargeback {
  transferId: string
  tenantId: string
  status: string
  transferType: string
  // absent when an operator closes the chargeback
  chargebackCode?: string
  messageSequence?: string
  controlNumber?: string
}

// transfer_incoming.undeliverable
interface TransferIncomingUndeliverable {
  undeliverableId: string
  tenantId: string
  jdIncomingMessageId: string
  rejectionReason: string
  rejectionCategory: string
  controlNumber: string
  messageSequence: string
  grossAmount: string
  senderISPB: string
}

// transfer_incoming.return_requested, returned, return_rejected, return_manual_review
interface TransferIncomingReturn {
  transferId: string // the return, a TED_OUT
  originalTransferId: string // the TED IN that it returns
  tenantId: string
  organizationId?: string
  transferType: string
  status: string
  controlNumber: string
  amount: string // the gross amount that leaves your institution
  returnReasonCode: string
  requestedVia: string
  externalReference?: string
  clearingControlNumber?: string
  reversalTransactionId?: string
  reconciliationReasonCode?: string
  reconciliationReasonClass?: string
  revertFailureClass?: string
  failureCode?: string
  rejectionCause?: "not_found" | "str_refused" | "jd_refused" | "local_verdict" | "operator"
  occurredAt: string
}

// transfer_outgoing.devolution_notified
interface TransferOutgoingDevolutionNotified {
  transferId: string
  tenantId: string
  codDevTransf: string
  chargebackCode: string
  senderISPB: string
  recipientISPB: string
  amount: string
  controlNumber: string
  transferStatus: string
  transferType: string
  feeRetained: boolean
  feeRefunded: boolean
  feeRefundedAmount?: string
  via: string
}

// transfer_internal.returned, return_failed
interface TransferInternalReturn {
  transferId: string // the return, a P2P
  originalTransferId: string
  tenantId: string
  organizationId: string
  ledgerId: string // the ledger of the debited account
  recipientLedgerId?: string // between ledgers only: the ledger of the credited account
  amount: string
  reversalTransactionId?: string
  requestedVia: string
  revertFailureClass?: string
  failureCode?: string
  occurredAt: string
}
```


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