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

> Configure TED across infrastructure, tenant, and account levels — limits, fee policies, and operating hours adjustable at runtime.

TED plugin configuration happens at three levels. Most business decisions can be changed at runtime without restarting the service.

## Configuration levels

***

| Level                        | Who manages  | What it controls                              | Requires restart? |
| ---------------------------- | ------------ | --------------------------------------------- | ----------------- |
| Infrastructure (env vars)    | DevOps       | URLs, credentials, authentication, timeouts   | Yes               |
| Tenant settings (Admin API)  | Product team | Limits, fee policy, operating hours overrides | No                |
| Account settings (Admin API) | Product team | Per-account limits and restrictions           | No                |

## Business decisions you can configure

***

These are the settings GPMs and product teams care about. All of them are managed via the Admin API at runtime — no deploy required.

### Transfer limits

Set daily and monthly volume caps at two levels:

* **Per tenant** — applies to all transfers across the organization
* **Per account** — applies to a specific end-user account (overrides tenant defaults)

Limits cover both total amount and number of transactions. Set these to manage risk and comply with BACEN requirements.

### Fee policy

Control whether the plugin charges a fee on TED OUT, TED IN, and P2P transfers. Fee rules are defined in the Fees Engine and applied per organization. See [Fees Engine](/en/midaz/plugins/fees-engine/fees-engine-overview) for configuration details.

### Fail-open vs. fail-closed

If the fee calculation service is unavailable at the moment of a transfer, you have two options:

* **Fail-open** — allow the transfer to proceed without charging a fee
* **Fail-closed** — block the transfer until the fee service is available again

The default is **fail-closed**. Change this per organization via the Admin API.

### TED IN receiving

Incoming transfers are **disabled by default**. Enable TED IN per organization once your JD SPB credentials are configured and the polling worker is active.

### Operating hours overrides

The plugin enforces BACEN's TED operating window by default. You can configure custom windows per tenant — for example, restricting transfers to business hours only — within BACEN limits.

## Infrastructure configuration

***

This section is for DevOps teams. These variables are set at deployment time and require a service restart to take effect.

### Application

| Variable                    | Default       | Required | Description                                                                                                                                                                                                                                                                                                                |
| --------------------------- | ------------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENV_NAME`                  | `development` |    No    | Environment (`development`, `staging`, `production`)                                                                                                                                                                                                                                                                       |
| `LOG_LEVEL`                 | `info`        |    No    | Log verbosity (`debug`, `info`, `warn`, `error`)                                                                                                                                                                                                                                                                           |
| `DEPLOYMENT_MODE`           | `byoc`        |    No    | Deployment flavor. `byoc` = Bring Your Own Cloud (single-tenant, operator-managed). Toggles internal SaaS vs. BYOC behaviors.                                                                                                                                                                                              |
| `SERVER_ADDRESS`            | `:8080`       |    No    | HTTP server address and port                                                                                                                                                                                                                                                                                               |
| `HTTP_BODY_LIMIT_BYTES`     | `1048576`     |    No    | Maximum HTTP request body size in bytes                                                                                                                                                                                                                                                                                    |
| `INFRA_CONNECT_TIMEOUT_SEC` | `30`          |    No    | Infrastructure services connection timeout in seconds                                                                                                                                                                                                                                                                      |
| `ALLOW_PRIVATE_UPSTREAMS`   | `false`       |    No    | ⚠️ Allow outbound adapters (CRM, Fees, JD, Midaz) to resolve to RFC1918/loopback IPs. Default `false` is fail-closed: production blocks DNS pivoting into private space. Enable in dev or in-cluster BYOC where upstreams legitimately live on private IPs. Cloud metadata endpoints stay blocked regardless of this flag. |

### TLS

| Variable                  | Default | Required | Description                                                                     |
| ------------------------- | ------- | :------: | ------------------------------------------------------------------------------- |
| `SERVER_TLS_CERT_FILE`    | -       |    No    | Path to TLS certificate file. Must be set together with `SERVER_TLS_KEY_FILE`.  |
| `SERVER_TLS_KEY_FILE`     | -       |    No    | Path to TLS private key file. Must be set together with `SERVER_TLS_CERT_FILE`. |
| `TLS_TERMINATED_UPSTREAM` | `false` |    No    | Set to `true` when TLS is terminated by a load balancer or reverse proxy.       |

### Proxy headers

Used when the service runs behind a load balancer or reverse proxy. Required to extract the real client IP for rate limiting and audit logs.

| Variable                 | Default |       Required      | Description                                                                                                         |
| ------------------------ | ------- | :-----------------: | ------------------------------------------------------------------------------------------------------------------- |
| `SERVER_PROXY_HEADER`    | -       |          No         | HTTP header carrying the real client IP (e.g. `X-Forwarded-For`, `X-Real-IP`). Empty disables proxy header parsing. |
| `SERVER_TRUSTED_PROXIES` | -       | If proxy header set | Comma-separated list of trusted proxy IPs/CIDRs. Required when `SERVER_PROXY_HEADER` is set to prevent IP spoofing. |

### CORS

| Variable               | Default                                                                                 | Required | Description                                                                                                |
| ---------------------- | --------------------------------------------------------------------------------------- | :------: | ---------------------------------------------------------------------------------------------------------- |
| `CORS_ALLOWED_ORIGINS` | `http://localhost:3000`                                                                 |    No    | Comma-separated list of allowed origins. Wildcards (`*`) and localhost origins are rejected in production. |
| `CORS_ALLOWED_METHODS` | `GET,POST,PUT,PATCH,DELETE,OPTIONS`                                                     |    No    | Allowed HTTP methods.                                                                                      |
| `CORS_ALLOWED_HEADERS` | `Origin,Content-Type,Accept,Authorization,X-Request-ID,X-Organization-Id,X-Idempotency` |    No    | Allowed request headers.                                                                                   |

### Authentication

This plugin delegates authorization to `plugin-auth`. Configure the connection with the variables below.

| Variable                    |          Default          | Required   | Description                                                                                       |
| --------------------------- | :-----------------------: | ---------- | ------------------------------------------------------------------------------------------------- |
| `PLUGIN_AUTH_ENABLED`       |          `false`          | No         | Enable authorization via plugin-auth. Set to `true` in production.                                |
| `PLUGIN_AUTH_ADDRESS`       |             -             | If enabled | URL of the plugin-auth service. Must use HTTPS in production.                                     |
| `PLUGIN_AUTH_CLIENT_ID`     | `plugin-br-bank-transfer` | If enabled | OAuth client ID used by this plugin.                                                              |
| `PLUGIN_AUTH_CLIENT_SECRET` |             -             | If enabled | OAuth client secret used by this plugin.                                                          |
| `AUTH_CACHE_TTL_SEC`        |            `60`           | No         | Duration (in seconds) for caching authorization decisions. Denied decisions are cached for TTL/4. |

<Warning>
  When `PLUGIN_AUTH_ENABLED=true`, `PLUGIN_AUTH_ADDRESS` must use HTTPS in production environments. HTTP addresses are rejected at startup.
</Warning>

### Test admin (non-production only)

<Warning>
  `BTF_TEST_ADMIN_ENABLED` must remain `false` in production. It exposes test-only admin endpoints (e.g. `POST /admin/test/circuit-breakers/reset`) that are tenant-less and bypass production authentication. Enabled only by docker-compose mock-lane for E2E tests; any deployment with this flag `true` outside a sealed test network is a misconfiguration.
</Warning>

| Variable                 | Default |     Required     | Description                                                                                                                                                             |
| ------------------------ | ------- | :--------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BTF_TEST_ADMIN_ENABLED` | `false` |        No        | ⚠️ Expose test-only admin endpoints. Must remain `false` in production.                                                                                                 |
| `BTF_TEST_ADMIN_TOKEN`   | -       | If admin enabled | Token required when `BTF_TEST_ADMIN_ENABLED=true`. Sent in the `X-Test-Admin-Token` header. Intentionally separate from production auth (test-only surface, no tenant). |

### Rate limiting

| Variable                | Default | Required | Description                                                      |
| ----------------------- | ------- | :------: | ---------------------------------------------------------------- |
| `RATE_LIMIT_ENABLED`    | `true`  |    No    | Enable per-client rate limiting. Forced to `true` in production. |
| `RATE_LIMIT_MAX`        | `100`   |    No    | Maximum requests per window per client.                          |
| `RATE_LIMIT_EXPIRY_SEC` | `60`    |    No    | Sliding window duration in seconds.                              |

### Idempotency

| Variable                       | Default | Required | Description                                                                                                                                              |
| ------------------------------ | ------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `IDEMPOTENCY_RETRY_WINDOW_SEC` | `300`   |    No    | Time window (in seconds) during which an idempotency key is considered valid.                                                                            |
| `IDEMPOTENCY_REQUIRE_REDIS`    | `false` |    No    | When `true`, reject requests if Redis is unavailable rather than falling back.                                                                           |
| `DUPLICATE_GUARD_TTL_SEC`      | `300`   |    No    | TTL (in seconds) for the Redis duplicate-guard window. Complements `IDEMPOTENCY_RETRY_WINDOW_SEC`; this TTL controls the duplicate-guard entry lifetime. |

### Multi-tenancy

| Variable                                        | Default                                                                |        Required        | Description                                                                                                                                                                                                                                                                                                                              |
| ----------------------------------------------- | ---------------------------------------------------------------------- | :--------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MULTI_TENANT_ENABLED`                          | `false`                                                                |           No           | Enable infrastructure-level multi-tenancy with tenant-isolated databases.                                                                                                                                                                                                                                                                |
| `DEFAULT_TENANT_ID`                             | `11111111-1111-1111-1111-111111111111`                                 |           No           | Fallback tenant UUID when auth is disabled. Must be a valid UUID.                                                                                                                                                                                                                                                                        |
| `DEFAULT_TENANT_SLUG`                           | `default`                                                              |           No           | Default tenant slug identifier.                                                                                                                                                                                                                                                                                                          |
| `DEFAULT_ORGANIZATION_ID`                       | -                                                                      |           No           | Organization ID used in single-tenant mode (when `MULTI_TENANT_ENABLED=false`). Complements `DEFAULT_TENANT_ID`.                                                                                                                                                                                                                         |
| `AWS_REGION`                                    | -                                                                      | If AWS secrets backend | AWS region for per-tenant secret reads from Secrets Manager. Required when `MULTI_TENANT_ENABLED=true` and the secrets backend is AWS.                                                                                                                                                                                                   |
| `MULTI_TENANT_INTEGRATION_SECRET_NAME_TEMPLATE` | `tenants/{env}/{tenantId}/{applicationName}/integration/configuration` |           No           | Template for the AWS Secrets Manager secret name. Placeholders: `{env}`, `{tenantId}`, `{applicationName}`.                                                                                                                                                                                                                              |
| `TENANT_IDS`                                    | -                                                                      |           No           | Comma-separated list of allowed tenant UUIDs. This single variable serves two purposes: (1) request allowlisting — requests from any tenant not in this list are rejected, and (2) licensing scope — only these tenants are considered licensed. Leave unset to allow all configured tenants. Also referenced under [License](#license). |
| `MULTI_TENANT_URL`                              | -                                                                      |   Yes (when enabled)   | Tenant Manager HTTP API URL.                                                                                                                                                                                                                                                                                                             |
| `MULTI_TENANT_REDIS_HOST`                       | -                                                                      |           No           | Redis host for Pub/Sub event-driven tenant discovery.                                                                                                                                                                                                                                                                                    |
| `MULTI_TENANT_REDIS_PORT`                       | `6379`                                                                 |           No           | Redis port for Pub/Sub.                                                                                                                                                                                                                                                                                                                  |
| `MULTI_TENANT_REDIS_PASSWORD`                   | -                                                                      |           No           | Redis password for Pub/Sub.                                                                                                                                                                                                                                                                                                              |
| `MULTI_TENANT_REDIS_TLS`                        | `false`                                                                |           No           | Enable TLS for Redis Pub/Sub connection.                                                                                                                                                                                                                                                                                                 |
| `MULTI_TENANT_TIMEOUT`                          | `30`                                                                   |           No           | HTTP timeout in seconds for Tenant Manager API calls.                                                                                                                                                                                                                                                                                    |
| `MULTI_TENANT_MAX_TENANT_POOLS`                 | `100`                                                                  |           No           | Maximum concurrent tenant database connection pools.                                                                                                                                                                                                                                                                                     |
| `MULTI_TENANT_IDLE_TIMEOUT_SEC`                 | `300`                                                                  |           No           | Idle timeout in seconds before evicting a tenant pool.                                                                                                                                                                                                                                                                                   |
| `MULTI_TENANT_CIRCUIT_BREAKER_THRESHOLD`        | `5`                                                                    |           No           | Number of failures before the circuit breaker opens.                                                                                                                                                                                                                                                                                     |
| `MULTI_TENANT_CIRCUIT_BREAKER_TIMEOUT_SEC`      | `30`                                                                   |           No           | Recovery timeout in seconds for the circuit breaker.                                                                                                                                                                                                                                                                                     |
| `MULTI_TENANT_SERVICE_API_KEY`                  | -                                                                      |   Yes (when enabled)   | API key for the Tenant Manager `/settings` endpoint.                                                                                                                                                                                                                                                                                     |
| `MULTI_TENANT_CACHE_TTL_SEC`                    | `120`                                                                  |           No           | In-memory tenant config cache TTL in seconds. Hot-reloadable via systemplane API.                                                                                                                                                                                                                                                        |
| `MULTI_TENANT_CONNECTIONS_CHECK_INTERVAL_SEC`   | `30`                                                                   |           No           | Async interval in seconds for revalidating pool settings. Bootstrap-only (not hot-reloadable).                                                                                                                                                                                                                                           |

**BYOC single-tenant:**

```bash theme={null}
DEFAULT_TENANT_ID=<your-tenant-uuid>
# Optional: restrict to specific tenant UUIDs
TENANT_IDS=<uuid1>,<uuid2>
```

**SaaS multi-tenant:**

```bash theme={null}
MULTI_TENANT_ENABLED=true
MULTI_TENANT_URL=http://tenant-manager:4003
MULTI_TENANT_SERVICE_API_KEY=your-api-key
MULTI_TENANT_REDIS_HOST=redis.example.com
```

### PostgreSQL

| Variable                           | Default                   | Required | Description                                    |
| ---------------------------------- | ------------------------- | :------: | ---------------------------------------------- |
| `POSTGRES_HOST`                    | `localhost`               |    Yes   | Primary PostgreSQL host.                       |
| `POSTGRES_PORT`                    | `5432`                    |    No    | Primary PostgreSQL port.                       |
| `POSTGRES_USER`                    | `plugin-br-bank-transfer` |    Yes   | Database user.                                 |
| `POSTGRES_PASSWORD`                | -                         |    Yes   | Database password. Required in production.     |
| `POSTGRES_DB`                      | `plugin-br-bank-transfer` |    Yes   | Database name.                                 |
| `POSTGRES_SSLMODE`                 | `require`                 |    No    | SSL mode. `disable` is rejected in production. |
| `POSTGRES_MAX_OPEN_CONNS`          | `25`                      |    No    | Maximum open connections.                      |
| `POSTGRES_MAX_IDLE_CONNS`          | `5`                       |    No    | Maximum idle connections.                      |
| `POSTGRES_CONN_MAX_LIFETIME_MINS`  | `30`                      |    No    | Connection max lifetime in minutes.            |
| `POSTGRES_CONN_MAX_IDLE_TIME_MINS` | `5`                       |    No    | Connection max idle time in minutes.           |
| `POSTGRES_CONNECT_TIMEOUT_SEC`     | `10`                      |    No    | Connection timeout in seconds.                 |
| `MIGRATIONS_PATH`                  | `migrations`              |    No    | Path to database migration files.              |

### PostgreSQL replica

Configure a read replica for query offloading. All fields fall back to primary values when unset.

| Variable                    | Default | Required | Description                       |
| --------------------------- | ------- | :------: | --------------------------------- |
| `POSTGRES_REPLICA_HOST`     | -       |    No    | Replica host. Unset = no replica. |
| `POSTGRES_REPLICA_PORT`     | -       |    No    | Replica port.                     |
| `POSTGRES_REPLICA_USER`     | -       |    No    | Replica user.                     |
| `POSTGRES_REPLICA_PASSWORD` | -       |    No    | Replica password.                 |
| `POSTGRES_REPLICA_DB`       | -       |    No    | Replica database name.            |
| `POSTGRES_REPLICA_SSLMODE`  | -       |    No    | Replica SSL mode.                 |

### MongoDB

MongoDB is required for transfer audit event persistence. The service will not start without a valid MongoDB connection.

| Variable                            | Default | Required | Description                                                                                                                                           |
| ----------------------------------- | ------- | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MONGO_ENABLED`                     | `true`  |    Yes   | Enable MongoDB connection. Must be `true` in all environments.                                                                                        |
| `MONGO_URI`                         | -       |    Yes   | MongoDB connection string (e.g. `mongodb://user:pass@host:27017`). Must include credentials and TLS in production.                                    |
| `MONGO_DATABASE`                    | -       |    Yes   | MongoDB database name.                                                                                                                                |
| `MONGO_MAX_POOL_SIZE`               | `25`    |    No    | Maximum connection pool size.                                                                                                                         |
| `MONGO_SERVER_SELECTION_TIMEOUT_MS` | `3000`  |    No    | Server selection timeout in milliseconds.                                                                                                             |
| `MONGO_HEARTBEAT_INTERVAL_MS`       | `10000` |    No    | Heartbeat interval in milliseconds.                                                                                                                   |
| `MONGO_TLS_CA_CERT`                 | -       |    No    | Base64-encoded CA certificate for MongoDB TLS. Use when MongoDB requires TLS with a custom CA (e.g. Atlas, private instances with self-signed certs). |

### Redis

| Variable                 | Default          | Required | Description                                     |
| ------------------------ | ---------------- | :------: | ----------------------------------------------- |
| `REDIS_HOST`             | `localhost:6379` |    Yes   | Redis host and port.                            |
| `REDIS_MASTER_NAME`      | -                |    No    | Redis Sentinel master name (if using Sentinel). |
| `REDIS_PASSWORD`         | -                |    No    | Redis password (if auth enabled).               |
| `REDIS_DB`               | `0`              |    No    | Redis database number.                          |
| `REDIS_PROTOCOL`         | `3`              |    No    | Redis protocol version (2 or 3).                |
| `REDIS_TLS`              | `false`          |    No    | Enable TLS for Redis connections.               |
| `REDIS_CA_CERT`          | -                |    No    | CA certificate for Redis TLS.                   |
| `REDIS_POOL_SIZE`        | `10`             |    No    | Connection pool size.                           |
| `REDIS_MIN_IDLE_CONNS`   | `2`              |    No    | Minimum idle connections.                       |
| `REDIS_READ_TIMEOUT_MS`  | `3000`           |    No    | Read timeout in milliseconds.                   |
| `REDIS_WRITE_TIMEOUT_MS` | `3000`           |    No    | Write timeout in milliseconds.                  |
| `REDIS_DIAL_TIMEOUT_MS`  | `5000`           |    No    | Dial timeout in milliseconds.                   |

<Warning>
  Redis is a mandatory dependency. If Redis is unavailable at startup or becomes unreachable at runtime, the service reports as DOWN to the orchestration readiness probe and stops accepting requests. Redis is required for idempotency key caching and duplicate detection.
</Warning>

### JD SPB connection

These variables are required for BYOC deployments. In SaaS mode, Lerian manages the JD connection.

| Variable                         | Default     |               Required               | Description                                                                                                                                                             |
| -------------------------------- | ----------- | :----------------------------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `JD_BASE_URL`                    | -           |                If BYOC               | JD SPB API base URL.                                                                                                                                                    |
| `JD_SOAP_PATH`                   | `/soap`     |                  No                  | JD SOAP endpoint path.                                                                                                                                                  |
| `JD_ORIGIN_ISPB`                 | -           |                If BYOC               | Originating ISPB code.                                                                                                                                                  |
| `JD_LEGACY_CODE`                 | -           |                If BYOC               | JD legacy system code (max 10 chars).                                                                                                                                   |
| `JD_USER_CODE`                   | -           |                If BYOC               | JD user code (max 10 chars).                                                                                                                                            |
| `JD_PASSWORD`                    | -           |                If BYOC               | JD password (encrypted at rest).                                                                                                                                        |
| `JD_PRIVATE_KEY_PEM`             | -           |                If BYOC               | RSA private key PEM content for XML signature.                                                                                                                          |
| `JD_PUBLIC_KEY_PEM`              | -           |                  No                  | Public key PEM for validating signatures on JD responses. Required when `JD_VALIDATE_EXTERNAL_SIGNATURE=true`.                                                          |
| `JD_PRIVATE_KEY_KEYINFO`         | -           |                  No                  | XML `<KeyInfo>` block embedded in the SOAP signature (e.g. base64-encoded X509 cert). Required for the WS-Security envelope when JD demands certificate identification. |
| `JD_SIGNING_MODE`                | `local_pem` |                  No                  | SOAP signing mode. `local_pem` signs locally with `JD_PRIVATE_KEY_PEM`; `external_signer` delegates to a remote service via `JD_EXTERNAL_SIGNER_URL`.                   |
| `JD_VALIDATE_EXTERNAL_SIGNATURE` | `true`      |                  No                  | Validate signatures on responses from JD (or the external signer). Default `true` keeps validation active. Set `false` only for debug/sandbox without configured PKI.   |
| `JD_EXTERNAL_SIGNER_URL`         | -           | If `JD_SIGNING_MODE=external_signer` | Base URL of the external signing service.                                                                                                                               |
| `JD_EXTERNAL_SIGNER_AUTH_TOKEN`  | -           |                  No                  | Bearer token sent in the `Authorization` header for external-signer calls.                                                                                              |
| `JD_EXTERNAL_SIGNER_TIMEOUT_MS`  | `5000`      |                  No                  | Timeout (in milliseconds) for external-signer calls.                                                                                                                    |
| `JD_BACEN_ROUTING_ISPB`          | `00038166`  |                  No                  | BACEN routing ISPB (the JD/JDSPB ISPB). Default `00038166` is the JD bank ISPB. Change only if the operator routes through a different PSP.                             |
| `JD_TIMEOUT_MS`                  | `8000`      |                  No                  | SOAP request timeout in milliseconds.                                                                                                                                   |
| `JD_MAX_RETRIES`                 | `3`         |                  No                  | Maximum retry attempts for JD requests.                                                                                                                                 |
| `JD_SANDBOX_MODE`                | `false`     |                  No                  | Enable JD sandbox mode. Rejected in production.                                                                                                                         |

### JD polling

| Variable                                 | Default | Required | Description                                                                                                                                                                                                                 |
| ---------------------------------------- | ------- | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `JD_POLLING_ENABLED`                     | `false` |    No    | Enable TED IN polling worker.                                                                                                                                                                                               |
| `JD_POLL_INTERVAL_SECONDS`               | `30`    |    No    | How often (in seconds) the plugin polls for incoming TEDs.                                                                                                                                                                  |
| `JD_POLL_MAX_MESSAGES_PER_CYCLE`         | `50`    |    No    | Maximum messages to process per polling cycle.                                                                                                                                                                              |
| `JD_POLL_MAX_RETRIES`                    | `3`     |    No    | Maximum retry attempts per polling cycle.                                                                                                                                                                                   |
| `JD_POLL_RECOVERY_BATCH_SIZE`            | `200`   |    No    | Batch size for recovery polling.                                                                                                                                                                                            |
| `JD_POLL_DISABLE_OPERATING_HOURS_WINDOW` | `true`  |    No    | When `true`, the RecebeMensagem poller ignores the operating-hours window (`TRANSFER_OPERATING_OPEN`/`CLOSE`) and runs continuously. Default `true` because JD can deliver messages outside the operator's transfer window. |
| `BTF_TED_IN_RETORNO_FILTER`              | -       |    No    | Comma-separated `TpRetorno` filter applied server-side by RecebeMensagem. Empty = no filter (pulls P/R/E/C). Note: the JD spec encodes `TpRetorno` as a single element, so only the first value is honored today.           |

<Note>
  `JD_POLLING_ENABLED` defaults to `false` for safer deployments. Before enabling it, ensure `DEFAULT_TENANT_ID` (or Pool Manager) is configured with a valid UUID. JD polling is not supported when `MULTI_TENANT_ENABLED=true`.
</Note>

### External services (Midaz)

| Variable                        | Default | Required | Description                                                                                                                                                                                              |
| ------------------------------- | ------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MIDAZ_BASE_URL`                | -       |    Yes   | Midaz base service URL.                                                                                                                                                                                  |
| `MIDAZ_TRANSACTION_URL`         | -       |    Yes   | Midaz transaction service URL.                                                                                                                                                                           |
| `MIDAZ_TIMEOUT_MS`              | `3000`  |    No    | Midaz request timeout in milliseconds.                                                                                                                                                                   |
| `MIDAZ_MAX_RETRIES`             | `3`     |    No    | Retry attempts on Midaz failure.                                                                                                                                                                         |
| `MIDAZ_OTEL_ENABLED`            | `true`  |    No    | Enable OpenTelemetry tracing for Midaz calls.                                                                                                                                                            |
| `MIDAZ_DEBUG`                   | `false` |    No    | Enable debug logging for Midaz calls. Rejected in production.                                                                                                                                            |
| `MIDAZ_AUTH_ENABLED`            | `false` |    No    | Enable M2M authentication for Midaz.                                                                                                                                                                     |
| `MIDAZ_AUTH_ADDRESS`            | -       |  If auth | Auth service URL for Midaz M2M tokens.                                                                                                                                                                   |
| `MIDAZ_CLIENT_ID`               | -       |  If auth | OAuth client ID for Midaz M2M.                                                                                                                                                                           |
| `MIDAZ_CLIENT_SECRET`           | -       |  If auth | OAuth client secret for Midaz M2M.                                                                                                                                                                       |
| `MIDAZ_BREAKER_FAILURES`        | `3`     |    No    | Circuit breaker failure threshold.                                                                                                                                                                       |
| `MIDAZ_BREAKER_TIMEOUT_SECONDS` | `30`    |    No    | Circuit breaker recovery timeout in seconds.                                                                                                                                                             |
| `BTF_MIDAZ_CB_ENABLED`          | `true`  |    No    | Kill switch for the Midaz client circuit breaker. When `false`, Midaz calls bypass the breaker (timeouts and retries remain active). Useful when intermittent flakes are amplifying into sustained 500s. |

### External services (CRM)

| Variable                             | Default | Required | Description                                                                                                                       |
| ------------------------------------ | ------- | :------: | --------------------------------------------------------------------------------------------------------------------------------- |
| `CRM_BASE_URL`                       | -       |    Yes   | CRM service URL.                                                                                                                  |
| `CRM_TIMEOUT_MS`                     | `2000`  |    No    | CRM request timeout in milliseconds.                                                                                              |
| `CRM_MAX_RETRIES`                    | `2`     |    No    | Retry attempts on CRM failure.                                                                                                    |
| `CRM_AUTH_ENABLED`                   | `false` |    No    | Enable M2M authentication for CRM.                                                                                                |
| `CRM_CLIENT_ID`                      | -       |  If auth | OAuth client ID for CRM M2M.                                                                                                      |
| `CRM_CLIENT_SECRET`                  | -       |  If auth | OAuth client secret for CRM M2M.                                                                                                  |
| `CRM_SENDER_LOOKUP_PATH_TEMPLATE`    | -       |    No    | Path template for sender lookup in the CRM (e.g. `/api/v1/accounts/{accountId}`). Placeholders are defined by the operator's CRM. |
| `CRM_RECIPIENT_LOOKUP_PATH_TEMPLATE` | -       |    No    | Path template for recipient lookup in the CRM.                                                                                    |
| `CRM_HOLDER_LOOKUP_PATH_TEMPLATE`    | -       |    No    | Path template for account-holder lookup in the CRM.                                                                               |

### External services (Fees)

<Note>
  `BTF_FEE_ENABLED` is the master switch. When `false` (default), no Fees adapter is constructed and every transfer proceeds with fee=0 with no HTTP call. The other `FEES_*` variables only take effect when `BTF_FEE_ENABLED=true`.
</Note>

| Variable                    | Default    |  Required  | Description                                                                                                                                                                                              |
| --------------------------- | ---------- | :--------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BTF_FEE_ENABLED`           | `false`    |     No     | Master switch for plugin-fees integration. When `false`, no Fees adapter is built and all transfers run with fee=0 without HTTP calls. Operators running plugin-fees must set this to `true` explicitly. |
| `FEES_BASE_URL`             | -          | If enabled | Fees Engine service URL.                                                                                                                                                                                 |
| `FEES_TIMEOUT_MS`           | `2000`     |     No     | Fee request timeout in milliseconds.                                                                                                                                                                     |
| `FEES_MAX_RETRIES`          | `2`        |     No     | Retry attempts on fee service failure.                                                                                                                                                                   |
| `FEES_FAIL_CLOSED_DEFAULT`  | `false`    |     No     | Default fee fail mode. `true` = block transfer when fees unavailable.                                                                                                                                    |
| `FEES_MAX_FEE_AMOUNT_CENTS` | `99999999` |     No     | Maximum fee amount in cents. Responses exceeding this value are rejected.                                                                                                                                |
| `FEES_REFUND_ON_DEVOLUCAO`  | `false`    |     No     | When `true`, charged fees are refunded on TED returns/reversals. Default `false` keeps the fee retained even on devolução.                                                                               |
| `FEES_AUTH_ENABLED`         | `false`    |     No     | Enable M2M authentication for Fees.                                                                                                                                                                      |
| `FEES_CLIENT_ID`            | -          |   If auth  | OAuth client ID for Fees M2M.                                                                                                                                                                            |
| `FEES_CLIENT_SECRET`        | -          |   If auth  | OAuth client secret for Fees M2M.                                                                                                                                                                        |

### Fees resilience

<Warning>
  `BTF_FEES_TED_IN_FAIL_OPEN` defaults to `true`. When plugin-fees is unavailable, inbound TEDs are credited with fee=0 (fail-open) so funds reach customers. Operators with an SLA that requires charging on every inbound credit must set this to `false` (fail-closed). TED OUT is unconditionally fail-closed regardless of this flag — the sender debit is authoritative.
</Warning>

| Variable                    | Default | Required | Description                                                                                                                                                  |
| --------------------------- | ------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `BTF_FEES_TED_IN_FAIL_OPEN` | `true`  |    No    | ⚠️ When `true`, TED IN proceeds with fee=0 if plugin-fees is unreachable. When `false`, TED IN is blocked until fees recover. TED OUT is always fail-closed. |

### Reconciliation

The reconciliation worker reaps pending transfers that never received a JD return and reconciles them against the ledger.

| Variable                             | Default | Required | Description                                                                                                                                                                         |
| ------------------------------------ | ------- | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BTF_RECONCILIATION_ENABLED`         | `true`  |    No    | Enable the internal reconciliation engine. When `false`, transfers without a JD return are never reconciled automatically.                                                          |
| `BTF_RECONCILIATION_INTERVAL_SEC`    | `30`    |    No    | Interval (in seconds) between reconciliation cycles.                                                                                                                                |
| `BTF_RECONCILIATION_BATCH_SIZE`      | `20`    |    No    | Maximum number of transfers processed per reconciliation cycle.                                                                                                                     |
| `BTF_RECONCILIATION_MAX_ATTEMPTS`    | `10`    |    No    | Maximum reconciliation attempts before marking a transfer as permanently failed.                                                                                                    |
| `BTF_RECONCILIATION_STALE_AFTER_SEC` | `60`    |    No    | Time (in seconds) after which a pending transfer is considered stale and eligible for reconciliation.                                                                               |
| `BTF_DLQ_ENABLED`                    | `true`  |    No    | Persist unparseable JD messages in the `jd_incoming_parse_failures` dead-letter table. Default `true` because dropping messages silently is never safe under at-most-once delivery. |

### Holiday calendar (BACEN/ANBIMA)

Drives the BACEN banking-holiday calendar used by the operating-hours gate and the initiation TTL clamp. The database is the source of truth; these variables only govern the scheduled refresher.

| Variable                    | Default                                                          | Required | Description                                                                                                                                                   |
| --------------------------- | ---------------------------------------------------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ANBIMA_HOLIDAY_SOURCE_URL` | `https://www.anbima.com.br/feriados/arqs/feriados_nacionais.xls` |    No    | Upstream URL for BACEN/ANBIMA holidays. The live fetcher is not yet wired (a static seed for 2026–2028 is used); operators may repoint to an internal mirror. |
| `ANBIMA_FETCHER_ENABLED`    | `true`                                                           |    No    | Toggle the scheduled holiday refresher. When `false`, the plugin relies on a pre-populated `bacen_holidays` table.                                            |
| `ANBIMA_FETCHER_SCHEDULE`   | `03:00`                                                          |    No    | Daily fire time (HH:MM) for the refresher. `03:00` sits outside BACEN hours and before the 06:30 window opens, avoiding mid-refresh snapshots.                |
| `HOLIDAY_CACHE_TTL`         | `1h`                                                             |    No    | TTL of the in-process holiday calendar cache (`time.ParseDuration` format: `1h`, `30m`, etc.). Lower this when manual UPSERTs need to propagate faster.       |

### RabbitMQ

Transfer lifecycle events can be published to RabbitMQ for downstream consumers.

| Variable                        | Default                   |  Required  | Description                                                      |
| ------------------------------- | ------------------------- | :--------: | ---------------------------------------------------------------- |
| `RABBITMQ_ENABLED`              | `false`                   |     No     | Enable transfer lifecycle event publishing.                      |
| `RABBITMQ_URL`                  | -                         | If enabled | AMQP connection URL. Must use `amqps://` outside development.    |
| `RABBITMQ_EXCHANGE`             | `bank_transfer.lifecycle` |     No     | Exchange name for lifecycle events.                              |
| `RABBITMQ_ROUTING_KEY_PREFIX`   | `transfer`                |     No     | Routing key prefix for published events.                         |
| `RABBITMQ_EVENT_SIGNING_SECRET` | -                         | If enabled | HMAC secret for signing published events. Minimum 32 characters. |
| `RABBITMQ_PUBLISH_TIMEOUT_MS`   | `5000`                    |     No     | Publish timeout in milliseconds.                                 |
| `RABBITMQ_MAX_RETRIES`          | `3`                       |     No     | Maximum publish retry attempts.                                  |
| `RABBITMQ_RETRY_BACKOFF_MS`     | `200`                     |     No     | Backoff between publish retries in milliseconds.                 |

### Webhook delivery

Outbound webhook delivery requires RabbitMQ to be enabled. The webhook worker consumes events from a RabbitMQ queue and delivers them to the configured endpoint.

| Variable                                   | Default                           |  Required  | Description                                                                                  |
| ------------------------------------------ | --------------------------------- | :--------: | -------------------------------------------------------------------------------------------- |
| `WEBHOOK_ENABLED`                          | `false`                           |     No     | Enable outbound webhook delivery. Requires `RABBITMQ_ENABLED=true`.                          |
| `WEBHOOK_ENDPOINT_URL`                     | -                                 | If enabled | Destination URL. Must use HTTPS; loopback/private IPs are rejected.                          |
| `WEBHOOK_SIGNING_SECRET`                   | -                                 | If enabled | HMAC secret for signing webhook payloads. Minimum 32 characters.                             |
| `WEBHOOK_BROKER_EVENT_SIGNING_SECRET`      | -                                 |     No     | Separate HMAC secret for verifying broker events. Falls back to `WEBHOOK_SIGNING_SECRET`.    |
| `WEBHOOK_ALLOW_UNSIGNED_BROKER_EVENTS`     | `false`                           |     No     | Temporarily accept unsigned broker events during migration.                                  |
| `WEBHOOK_UNSIGNED_BROKER_EVENTS_GRACE_SEC` | `0`                               |     No     | Grace period (in seconds) for unsigned events. Must be > 0 when unsigned events are allowed. |
| `WEBHOOK_QUEUE_NAME`                       | `transfer.webhook.delivery`       |     No     | RabbitMQ queue name for webhook events.                                                      |
| `WEBHOOK_DLQ_NAME`                         | `transfer.webhook.dlq`            |     No     | Dead-letter queue for failed webhook deliveries.                                             |
| `WEBHOOK_CONSUMER_TAG`                     | `plugin-br-bank-transfer-webhook` |     No     | RabbitMQ consumer tag.                                                                       |
| `WEBHOOK_PREFETCH_COUNT`                   | `20`                              |     No     | RabbitMQ prefetch count.                                                                     |
| `WEBHOOK_DELIVERY_CONCURRENCY`             | `8`                               |     No     | Maximum concurrent webhook deliveries per worker.                                            |
| `WEBHOOK_TIMEOUT_MS`                       | `5000`                            |     No     | HTTP delivery timeout in milliseconds.                                                       |
| `WEBHOOK_MAX_RETRIES`                      | `3`                               |     No     | Maximum delivery retry attempts.                                                             |
| `WEBHOOK_RETRY_BACKOFF_MS`                 | `500`                             |     No     | Backoff between delivery retries in milliseconds.                                            |

### Usage limits

| Variable                    | Default | Required | Description                                                                         |
| --------------------------- | ------- | :------: | ----------------------------------------------------------------------------------- |
| `USAGE_LIMITS_ENABLED`      | `false` |    No    | Enable per-account usage limit enforcement.                                         |
| `USAGE_LIMIT_DAILY_CENTS`   | `0`     |    No    | Default daily transfer limit in cents. At least one limit must be > 0 when enabled. |
| `USAGE_LIMIT_MONTHLY_CENTS` | `0`     |    No    | Default monthly transfer limit in cents.                                            |

### Operating hours

| Variable                      | Default             | Required | Description                                    |
| ----------------------------- | ------------------- | :------: | ---------------------------------------------- |
| `TRANSFER_OPERATING_OPEN`     | `06:30`             |    No    | Transfer acceptance window open time (HH:MM).  |
| `TRANSFER_OPERATING_CLOSE`    | `17:00`             |    No    | Transfer acceptance window close time (HH:MM). |
| `TRANSFER_OPERATING_TIMEZONE` | `America/Sao_Paulo` |    No    | Timezone for operating hours evaluation.       |

### Telemetry (OpenTelemetry)

| Variable                                 | Default                                           | Required | Description                                                                                                                                                                                                                                    |
| ---------------------------------------- | ------------------------------------------------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENABLE_TELEMETRY`                       | `false`                                           |    No    | Enable OpenTelemetry tracing and metrics.                                                                                                                                                                                                      |
| `OTEL_RESOURCE_SERVICE_NAME`             | `plugin-br-bank-transfer`                         |    No    | OTel service name.                                                                                                                                                                                                                             |
| `OTEL_LIBRARY_NAME`                      | `github.com/LerianStudio/plugin-br-bank-transfer` |    No    | OTel instrumentation library name.                                                                                                                                                                                                             |
| `OTEL_RESOURCE_SERVICE_VERSION`          | `1.0.0`                                           |    No    | OTel service version.                                                                                                                                                                                                                          |
| `OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT`   | `development`                                     |    No    | OTel deployment environment.                                                                                                                                                                                                                   |
| `OTEL_EXPORTER_OTLP_ENDPOINT`            | `localhost:4317`                                  |    No    | OTel collector gRPC endpoint.                                                                                                                                                                                                                  |
| `DB_METRICS_INTERVAL_SEC`                | `15`                                              |    No    | Database metrics collection interval in seconds.                                                                                                                                                                                               |
| `RECONCILIATION_PENDING_ALERT_THRESHOLD` | `5`                                               |    No    | Alert threshold for pending reconciliation items.                                                                                                                                                                                              |
| `OTEL_TRACES_SAMPLER_ARG`                | -                                                 |    No    | Trace sampling ratio (0.0–1.0). Unset uses the default sampler resolved at telemetry init: `0.1` in production, `1.0` elsewhere. Values outside (0,1] are clamped to 1.0 so misconfiguration cannot silently disable production tracing.       |
| `BTF_METRICS_PROMETHEUS_ENABLED`         | `false`                                           |    No    | Expose a dedicated Prometheus scrape endpoint for `btf.*` metrics. When `true`, the listener at `BTF_METRICS_PROMETHEUS_ADDRESS` is started.                                                                                                   |
| `BTF_METRICS_PROMETHEUS_ADDRESS`         | `127.0.0.1:9090`                                  |    No    | Listen address for the Prometheus `/metrics` endpoint. Default binds to loopback so a misconfigured pod does not expose unauthenticated metrics to the cluster network. Override (e.g. `0.0.0.0:9090`) only behind a NetworkPolicy or sidecar. |

### License

| Variable                  | Default |    Required   | Description                                                                                                                                       |
| ------------------------- | ------- | :-----------: | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `LICENSE_KEY`             | -       | In production | License key. Required in production environments.                                                                                                 |
| `LICENSE_SERVICE_ADDRESS` | -       |       No      | License validation service URL.                                                                                                                   |
| `TENANT_IDS`              | -       |       No      | Same variable as in [Multi-tenancy](#multi-tenancy). Comma-separated list of tenant UUIDs used for both request allowlisting and licensing scope. |

### Encryption

Field-level encryption for sensitive data at rest. Each key must be a base64-encoded 32-byte AES-256 key. Leave empty to disable encryption for that field.

| Variable                             | Default | Required | Description                                           |
| ------------------------------------ | ------- | :------: | ----------------------------------------------------- |
| `JD_INCOMING_RAW_XML_ENCRYPTION_KEY` | -       |    No    | AES-256 key for encrypting incoming JD XML payloads.  |
| `RECIPIENT_DETAILS_ENCRYPTION_KEY`   | -       |    No    | AES-256 key for encrypting recipient details at rest. |

## Runtime configuration (Admin API)

***

Tenant-level and account-level settings are managed via the Admin API — no restart required. Changes take effect immediately (subject to cache TTL for tenant settings).

Configurable settings include:

* Transfer limits (daily and monthly, per tenant and per account)
* Fee behavior (fail-open or fail-closed when the fee service is unavailable)
* TED IN receiving (enabled or disabled per organization)
* Operating hours overrides (custom windows within BACEN limits)

See the Admin API reference for the full list of configurable fields and request format.
