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

# Prerequisites

> What must be true before your first Lender call — the services it depends on, the two migration sets, the configuration that origination requires, and the token that carries the officer.

Lender needs a small set of things in place before its first API call. This page lists them in the order you set them up. When every item here is true, follow the [Quick start](/en/lender/lender-quick-start) to originate a loan.

## Services Lender depends on

***

| Service                       | Version | Why Lender needs it                                                                          |
| ----------------------------- | ------- | -------------------------------------------------------------------------------------------- |
| **PostgreSQL**                | 17      | The primary data store. Every product, application, schedule, and posting intent lives here. |
| **Valkey** (Redis-compatible) | 8       | Idempotency records, cache, and rate limiting.                                               |
| **Identity provider**         | —       | Issues the bearer tokens Lender authorizes each route against.                               |
| **Midaz**                     | —       | The ledger the disbursement posting reaches. Optional for your first loan.                   |
| **RedPanda**                  | —       | The streaming backbone for the event catalog. Optional.                                      |

PostgreSQL, Valkey, and an identity provider are the three you cannot originate without. A first loan needs no Midaz and no RedPanda. Read [Ledger postings](#ledger-postings) below for what waits.

## Run Lender locally

***

Lender is a single Go service. A local run needs the Go toolchain at 1.26 or later, Docker with Compose, and Make.

<Steps>
  <Step title="Create the environment file">
    `make set-env` copies `config/.env.example` to `config/.env`. Edit that file for every setting on this page.
  </Step>

  <Step title="Start the dependencies">
    `make up` starts the Compose dependencies and the service on port `8080`.
  </Step>

  <Step title="Apply the migrations">
    `make migrate-up` applies both migration sets. See the next section.
  </Step>

  <Step title="Run with live reload">
    `make dev` runs the service with Air.
  </Step>
</Steps>

## Apply both migration sets

***

Lender does not migrate the database when it starts. You apply migrations out of band, and there are two sets:

| Setting              | Default                                | Contents                                                                 |
| -------------------- | -------------------------------------- | ------------------------------------------------------------------------ |
| `MIGRATIONS_PATH`    | `migrations`                           | The core schema — products, applications, schedules, accounting, outbox. |
| `MIGRATIONS_BR_PATH` | `internal/jurisdictions/br/migrations` | The Brazil jurisdiction schema.                                          |

Apply both. `make migrate-up` reads both paths.

The core set also seeds the **jurisdiction registry**: two active jurisdictions, `BR` for Brazil and `XX`, the generic profile. No API creates a jurisdiction. The deployed binary carries each profile's behavior, and the table records which codes that binary knows. Read [Jurisdictions](/en/lender/jurisdictions) for what a profile decides.

## Required configuration

***

Two settings gate origination. Set both before the first call.

### An authenticated subject — `PLUGIN_AUTH_ENABLED=true`

Lender records who acted on every loan application. It reads the **assigned officer from the bearer token's subject**, not from the request body. Every loan-application call therefore needs an authenticated identity.

Set two variables:

| Variable              | Value                                  |
| --------------------- | -------------------------------------- |
| `PLUGIN_AUTH_ENABLED` | `true`                                 |
| `PLUGIN_AUTH_HOST`    | The address of your identity provider. |

Then send a bearer token on every call. The token needs a non-empty subject claim, and under the generic `XX` profile the same subject must create, approve, and disburse the application.

`make generate-casdoor` writes Lender's roles and permissions to `config/casdoor/init_data.json`. Load that seed into your identity provider. The officer's role needs these permissions:

| Resource            | Actions                                             |
| ------------------- | --------------------------------------------------- |
| `loan_product`      | `write`                                             |
| `accounting`        | `write`                                             |
| `loan_applications` | `preview:schedule`, `create`, `approve`, `disburse` |
| `loan_accounts`     | `read`                                              |

### A durable posting sink — `OUTBOX_ENABLED=true`

A disbursed loan must record its posting intent durably. Lender writes that intent to a transactional outbox in the same database transaction that moves the application to `disbursed`. Set `OUTBOX_ENABLED=true` so the outbox is available when you disburse.

The dispatcher relays each intent afterwards, on the cadence `OUTBOX_DISPATCH_INTERVAL_SEC` sets. Read [Accounting and accrual runs](/en/lender/accounting-and-accrual-runs) for what the ledger receives.

## Ledger postings

***

Point Lender at Midaz when you want postings to land in the ledger:

| Variable                                          | Purpose                                           |
| ------------------------------------------------- | ------------------------------------------------- |
| `MIDAZ_LEDGER_BASE_URL`                           | The ledger address.                               |
| `MIDAZ_OAUTH_TOKEN_URL`                           | Where Lender gets its ledger credentials.         |
| `MIDAZ_LEDGER_ORGANIZATION_ID`, `MIDAZ_LEDGER_ID` | The default posting target in single-tenant mode. |

The loan originates with or without these. Until you configure a ledger endpoint, posting intents stay durable in the outbox and the ledger entry waits. Configure the ledger before you expect bookings.

## Single-tenant defaults

***

`MULTI_TENANT_ENABLED` is `false` by default. In that mode `DEFAULT_TENANT_ID` identifies the one tenant, and it must be a valid UUID.

## Multi-tenant additions

***

Multi-tenant mode gives each tenant its own schema and its own ledger client pool. It adds four requirements:

1. Set `MULTI_TENANT_ENABLED=true` and point Lender at the tenant manager.
2. Every token carries a `tenantId` claim.
3. `public.tenant_jurisdictions` carries a row for each tenant and jurisdiction pair.
4. Each accounting profile declares its own `midazOrganizationId` and `midazLedgerId`. Multi-tenant mode does not fall back to the environment defaults.

## Event streaming

***

Streaming is off by default and origination does not need it. To publish the lifecycle event catalog, set `STREAMING_ENABLED=true`, point `STREAMING_BROKERS` at RedPanda, and set `STREAMING_CLOUDEVENTS_SOURCE=lender`. Lender validates that source value when it starts.

## Checklist

***

* PostgreSQL 17 and Valkey 8 reachable.
* Both migration sets applied.
* `PLUGIN_AUTH_ENABLED=true` and the identity provider reachable.
* A bearer token with a subject and every permission in the table above.
* `OUTBOX_ENABLED=true`.
* `DEFAULT_TENANT_ID` a valid UUID, in single-tenant mode.

## Next steps

***

<Card title="Quick start" icon="rocket" href="/en/lender/lender-quick-start" horizontal>
  Six calls from an empty database to a disbursed loan.
</Card>

<Card title="Configuration and deploy" icon="sliders" href="/en/lender/configuration-and-deploy" horizontal>
  The full container shape, the wider environment surface, and the ledger posting path.
</Card>
