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

# Lender events

> Subscribe to the credit journey: the fixed CloudEvents source, the topic and ce-type shapes, the 21 events of the documented journey, the outbox durability guarantee, and what a payload carries.

Lender publishes a business event whenever a product, an application, a loan account, or a Brazilian regulatory record changes state. Subscribe to those events and your service reacts to each change as it happens, with no polling loop.

Every event is outbox-backed and tenant-scoped. This page covers the consumer side: what arrives on the wire, which events exist, and how to make a handler safe.

## Turn publication on

***

Event publication is a deployment choice. Origination works without it.

| Setting                        | Value                                                                     |
| ------------------------------ | ------------------------------------------------------------------------- |
| `STREAMING_ENABLED`            | `true`                                                                    |
| `STREAMING_BROKERS`            | The RedPanda addresses.                                                   |
| `STREAMING_CLOUDEVENTS_SOURCE` | `lender`. Lender requires exactly this value and validates it at startup. |
| `OUTBOX_ENABLED`               | `true`. Every event is outbox-backed, so publication needs the outbox.    |

Lender refuses to start when publication is on and any of these is wrong, so a misconfigured deployment fails at startup instead of dropping events quietly.

## The wire contract

***

The CloudEvents source is the fixed literal `lender`, and the source also namespaces every topic. A subscriber therefore reads:

| Wire element        | Value                                                |
| ------------------- | ---------------------------------------------------- |
| Kafka topic         | `lender.<resource>.<event>`                          |
| `ce-source` header  | `lender`                                             |
| `ce-type` header    | `studio.lerian.<resource>.<event>`                   |
| `ce-subject` header | The identifier of the record that changed.           |
| Partition key       | The tenant, so one tenant's events keep their order. |

For a disbursement that resolves to:

```
topic    lender.loan_application.disbursed
ce-type  studio.lerian.loan_application.disbursed
```

Messages travel in CloudEvents binary mode, version 1.0. The context attributes ride as message headers: `ce-specversion`, `ce-id`, `ce-source`, `ce-type`, `ce-time`, `ce-schemaversion`, `ce-resourcetype`, and `ce-eventtype` on every message, plus `ce-subject`, `ce-datacontenttype`, and `ce-tenantid` when Lender has a value for them.

`ce-schemaversion` is `1.0.0` for every event in the catalog. The topic carries no version suffix at this schema version, so bind to the plain topic name.

<Info>
  **Subscribe by topic.** The topic is the address, and it composes from the resource type and the event type — not from an internal catalog name. Read the topic column in the tables below rather than deriving one from an event description.
</Info>

## The events

***

This page covers the **21 events of the documented credit journey**. They group by the part of the journey they report. The streaming manifest declares your deployment's whole catalog, which can carry more definitions than this page lists.

### Products

| Topic                                  | Emitted when                                            |
| -------------------------------------- | ------------------------------------------------------- |
| `lender.loan_product.created`          | A product is created as a draft definition.             |
| `lender.loan_product.activated`        | A product moves to active and pins its current version. |
| `lender.loan_product_version.created`  | A version snapshot is appended as immutable terms.      |
| `lender.accounting_profile.configured` | A profile and its posting rules are bound to a version. |
| `lender.loan_charge.applied`           | A product-version charge is applied to a loan account.  |

### Origination

| Topic                               | Emitted when                                                       |
| ----------------------------------- | ------------------------------------------------------------------ |
| `lender.loan_application.submitted` | An application is accepted and waits for approval.                 |
| `lender.loan_application.approved`  | An application is approved, with the decision facts.               |
| `lender.loan_application.rejected`  | An application is rejected, with the decision facts.               |
| `lender.loan_application.withdrawn` | A pending application is withdrawn.                                |
| `lender.loan_application.disbursed` | An approved application is disbursed and a loan account goes live. |

### Servicing

| Topic                                     | Emitted when                                          |
| ----------------------------------------- | ----------------------------------------------------- |
| `lender.repayment.recorded`               | A repayment is recorded, with its cash allocation.    |
| `lender.repayment_reversal.recorded`      | A reversal is recorded as a compensating transaction. |
| `lender.loan_schedule.prepayment_applied` | A prepayment produces a successor schedule version.   |
| `lender.loan_schedule.rescheduled`        | A reschedule produces a successor schedule version.   |

### Brazil pack

| Topic                                        | Emitted when                                                              |
| -------------------------------------------- | ------------------------------------------------------------------------- |
| `lender.loan_account.pdd_stage_transitioned` | A PDD stage transition or cure is recorded, with its accrual eligibility. |
| `lender.prepayment_quote.created`            | An immutable prepayment quote is created.                                 |
| `lender.prepayment_settlement.recorded`      | An accepted quote settles, with the final breakdown.                      |

### Consignado privado

| Topic                                          | Emitted when                                                                    |
| ---------------------------------------------- | ------------------------------------------------------------------------------- |
| `lender.consignado_exclusao.requested`         | Lender asks the payroll rail to cancel an averbação.                            |
| `lender.consignado_redirecionamento.requested` | Lender asks the payroll rail to redirect collection to another employment link. |
| `lender.payroll_deduction.refund_required`     | Confirmed payroll cash arrived after payoff and the borrower is owed a refund.  |
| `lender.guarantee_recovery_cash.allocated`     | A confirmed guarantee-recovery receipt is allocated.                            |

The consignado journey also **consumes** facts from the payroll-deduction gateway, on that product's own topics. Read [Consignado privado](/en/lender/consignado-privado).

## Durability and delivery

***

Every event in the catalog carries the same delivery policy. Nothing publishes straight to the broker. Lender writes the event to a transactional outbox in the **same database transaction** as the state change, and a dispatcher relays it afterwards. The event therefore survives a crash, and it survives a broker outage while the dispatcher retries.

The retry budget is bounded. The dispatcher gives one event `OUTBOX_MAX_DISPATCH_ATTEMPTS` attempts, and the default is `10`. It waits `OUTBOX_RETRY_WINDOW_SEC` seconds between attempts, and the default is `300`. Past that budget the dispatcher stops retrying the event. Raise the budget when you expect outages longer than the default window.

Events ride the same outbox as the ledger relay, so the local state change and the event commit together in a single database transaction. The posting intent joins them when the change produces one. The ledger posting lands afterwards. Read [Accounting and accrual runs](/en/lender/accounting-and-accrual-runs).

<Warning>
  **Delivery is at-least-once.** An event can arrive more than once, and a redelivery carries the same facts. Make each handler idempotent on the record identifier in the payload, which also travels in `ce-subject`.
</Warning>

## What a payload carries

***

The body is JSON and carries the facts of the change, not a diff. A `loan_application.disbursed` body:

```json theme={null}
{
  "loanApplicationId": "018f2a1c-7d40-7b31-9d40-2f1e8c5a4b77",
  "loanProductVersionId": "8f2c1a9e-4b30-4c02-9a1b-2d5e6f7a1c33",
  "borrowerId": "borrower-0001",
  "assignedOfficerId": "officer-0007",
  "status": "disbursed",
  "loanAccountId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "disbursementEventId": "019826f4-6a9c-7b31-8a02-11c3d4e5f678",
  "grossRequestedAmount": "48000.00",
  "netDeliveredAmount": "48000.00",
  "disbursedAt": "2026-08-01T13:00:00Z",
  "previewJurisdictionCode": "XX",
  "updatedAt": "2026-08-01T13:00:00Z"
}
```

**Money and rates travel as decimal strings**, never as JSON numbers, exactly as they do over REST. Parse them with a decimal type. Timestamps are RFC 3339 in UTC.

Each event carries the facts its own record holds, so read the shape of the event you subscribe to. A Brazilian event adds what its regulation needs: a PDD transition carries the stage and the accrual eligibility, and a prepayment quote carries the rebate and the IOF reconciliation.

## Check the contract at startup

***

`GET /api/v1/streaming/manifest` returns the catalog manifest: every event definition with its resource type, event type, schema version, and delivery policy. It needs a bearer token with `streaming_manifest` `read`. It reports the deployment's whole declared catalog, not one tenant's view.

Read it when your consumer starts. Check each event you subscribe to against the manifest: the resource type, the event type, and the schema version. That catches a name or version mismatch at startup instead of at runtime.

## Next steps

***

<CardGroup cols={2}>
  <Card title="Lender REST API" icon="code" href="/en/lender/lender-rest-api">
    The base path, authentication, idempotency, and the operations by job.
  </Card>

  <Card title="Lender architecture" icon="sitemap" href="/en/lender/lender-architecture">
    The parts of the service, the outbox path, and the time-driven work.
  </Card>

  <Card title="Lender in the platform" icon="diagram-project" href="/en/lender/lender-in-the-platform">
    Where Lender sits next to Midaz, Access Manager, and the streaming backbone.
  </Card>

  <Card title="Consignado privado" icon="brazilian-real-sign" href="/en/lender/consignado-privado">
    The payroll-deduction journey and the facts it consumes.
  </Card>
</CardGroup>
