Appearance
Environment Variables
See repository root .env.example. For the packaged production stack, copy deploy/pay/.env.example to .env; deploy/pay/docker-restart.sh refuses to start while any CHANGE_ME value remains.
| Variable | Description |
|---|---|
POSTGRES_DB | Database name |
POSTGRES_USER | Database user |
POSTGRES_PASSWORD | Database password |
REDIS_PASSWORD | Redis Stack password used by local compose stacks; dev-only value belongs in local env files, never use a production secret in git |
ConnectionStrings__Redis | Redis connection string for runtime invite/session state (local dev host: localhost:6379; password is supplied via Redis__Password) |
Redis__Configuration | Optional Redis connection string override; when unset, ConnectionStrings__Redis is used |
Redis__Password | Redis password override for the API; in local compose it must match REDIS_PASSWORD |
Redis__Database | Optional Redis logical database number for runtime stores and index initializers |
Redis__AbortConnect | Optional StackExchange.Redis abort-connect flag; default false for resilient startup |
JWT_SIGNING_KEY | JWT signing key (min 32 chars in prod) |
LOG_DIRECTORY | Directory for Serilog rolling files (api-YYYYMMDD.log, api-errors-…, gateway-…). Default when unset: backend/logs. Prefer absolute path in production. See docs/docs/backend/logging.md. |
LOG_ARCHIVE_DIR | Optional archive directory for rotated logs; default {LOG_DIRECTORY}/archive |
ASPNETCORE_ENVIRONMENT | Use Development to enable Debug Pay layers and Verbose IikoConnector (Serilog Trace). |
PaySecrets__EncryptionKey | Required when storing local Pay-managed secrets such as Syrve connection passwords; 32-byte AES key as base64, 64-char hex, or 32-byte UTF-8 value; never commit production keys |
PaySecrets__KeyId | Optional key identifier stored with encrypted local secret records; default local-v1 |
SEED_ADMIN_EMAIL | Dev seed admin email |
SEED_ADMIN_PASSWORD | Dev seed admin password |
BankFiles__RootPath | Controlled server-side root directory for temporary original bank file storage; default data/bank-files; never expose this path through API responses |
BankFiles__RetentionHours | Original bank file retention in hours; default 24 for MHPAY-61 |
BankFiles__Cleanup__Enabled | Enables the background original bank file retention cleanup worker; default true |
BankFiles__Cleanup__IntervalSeconds | Background original bank file retention cleanup interval in seconds; default 300 |
VITE_API_BASE_URL | Frontend API base (empty in prod same-origin) |
VITE_DOCS_URL | Documentation portal URL |
CORS__ALLOWEDORIGINS__0 | Allowed CORS origin(s) |
Platform operational settings (env bootstrap)
Env defaults apply when the PlatformSettings DB row is empty or a field is null. DB overrides are managed via /api/platform/settings and /settings UI. Secrets (JWT, Postgres, seed admin, Syrve passwords) are not part of this layer.
| Variable | Default | Description |
|---|---|---|
Platform__BankStatementUploadEnabled | true | Default allow bank statement upload before org quota |
Platform__MatchingDateToleranceDays | 14 | Matching date tolerance (falls back to Syrve__Matching__DateToleranceDays when unset in DB) |
Platform__MatchingExactAmountTolerance | 0.01 | Matching amount tolerance (falls back to Syrve__Matching__ExactAmountTolerance when unset in DB) |
Telegram bank-statement inbox
| Variable | Default | Description |
|---|---|---|
Telegram__Enabled | false | Feature flag for Telegram inbox (link + webhook/polling + upload) |
Telegram__BotUsername | empty | Bot username without @ (for deep links) |
Telegram__BotToken | empty | BotFather token; required when Enabled=true; never commit |
Telegram__WebhookSecret | empty | Must match setWebhook secret_token; required when Enabled and not UsePolling |
Telegram__UsePolling | false | Long polling via getUpdates (local tuna only). On a public host set false so the API registers setWebhook |
Telegram__NotifyOnRecognition | true | Push recognition/parse summaries to linked chat |
Telegram__AllowPostFromBot | false | Allow PaymentRun approve from bot callbacks |
Telegram__PublicAppBaseUrl | http://localhost:5000 | SPA origin for deep links; do not append /api (trailing /api is stripped) |
Telegram__ChallengeTtlMinutes | 10 | One-time personal link code TTL |
Telegram__WebhookPublicUrl | unset | Public webhook URL; default {PublicAppBaseUrl}/api/telegram/webhook. Duplicate /api/api/ is collapsed |
Operational settings still resolve as env defaults → platform DB → organization quota → location override (future) for supported settings. Legacy Syrve/iiko capability columns in platform/quota tables are deprecated and ignored; they do not disable registered Pay-host runtime capabilities.
Syrve orchestration
| Variable | Default | Description |
|---|---|---|
Syrve__PostingMaxRetryAttempts | 5 | Max posting retries |
Syrve__PostingRetryBaseDelaySeconds | 60 | Base retry delay |
Syrve__Reconciliation__ReadbackRetryDelaysSeconds__0 | 5 | First readback retry delay (seconds) |
Syrve__Reconciliation__ReadbackRetryDelaysSeconds__1 | 30 | Second readback retry delay |
Syrve__Reconciliation__ReadbackRetryDelaysSeconds__2 | 120 | Third readback retry delay |
Syrve__Reconciliation__ReadbackRetryDelaysSeconds__3 | 300 | Fourth readback retry delay |
Syrve__Reconciliation__EscalateAfterAttempts | 4 | Create anomaly after this many failed reconciliation attempts |
Syrve__DocumentSync__IntervalSeconds | 300 | Sync interval |
Syrve__DocumentSync__LookbackDays | 90 | Document lookback window |
Syrve__DocumentSync__MaxBatchSize | 100 | Global maximum documents per sync run across all configured document types |
Syrve__DocumentSync__MaxConcurrentDocumentFetches | 4 | Maximum parallel per-document reads in bounded fallback/hybrid enrichment |
Syrve__DocumentSync__OutgoingUnpaidRefreshMinutes | 60 | Reuse a fresh unchanged Outgoing snapshot for normal UI/worker sync; expired/modified/missing snapshots and explicit forceRefresh=true perform exact bounded unpaid reads |
Syrve__DocumentSync__FetchDocumentDetails | true | Persist full document/line details from unpaid batch; fallback calls document.getDocument only when local metadata/details are missing |
Syrve__DocumentSync__PreferUnpaidBatch | true | Try getUnpaidInvoicesByDepartments first; unusable/failed responses automatically fall back without mass Paid transitions |
Syrve__DocumentSync__EmptyBatchActiveListThreshold | 0 | Maximum active list size for accepting an empty batch without prior positive batch evidence or outstanding snapshots |
Syrve__DocumentSync__UnpaidBatchDepartmentChunkSize | unset | Optional department chunk size for unpaid batch requests; each chunk is capped by the connector request limit |
Syrve__DocumentSync__UnpaidBatchDepartmentIds__0 | unset | Optional first department UUID for CHAIN batch chunking; add indexed values as needed |
Syrve__SupplierSync__IntervalSeconds | 3600 | Supplier sync interval |
Syrve__SupplierSync__MaxBatchSize | 1000 | Local supplier apply batch; not sent to iiko and not network pagination |
Syrve__SupplierSync__StaleAfterHours | 24 | Mark suppliers inactive when missing from sync |
Syrve__Matching__DebitDocumentTypes__0 | IncomingInvoice | Document types for bank debits |
Syrve__Matching__DebitDocumentTypes__1 | ServiceReceipt | Additional debit types |
Syrve__Matching__CreditDocumentTypes__0 | OutgoingInvoice | Document types for bank credits |
Syrve__Matching__CreditDocumentTypes__1 | ServiceProvision | Additional credit types |
Syrve__Matching__DateToleranceDays | 14 | Candidate date proximity window |
Syrve__Matching__ExactAmountTolerance | 0.01 | Amount equality tolerance |
Always-on Pay-host runtime
Capabilities registered by AddIikoConnectorForPayHost() are always enabled: live reads, document/supplier sync, and the worker-only payment posting path do not have env/config capability switches. New postings always start in ApprovalRequired; only POST /api/syrve/postings/{id}/approve can release them to worker execution.
The following removed keys must not be added to deployment profiles: Syrve__UseIikoConnector, Syrve__RuntimeCallsEnabled, Syrve__PaymentWriteEnabled, Syrve__PaymentWriteRequiresApproval, Syrve__AutoQueuePostingOnConfirm, Syrve__AllowTestEnvironmentWrites, Syrve__AllowProductionWrites, Syrve__DocumentSync__Enabled, Syrve__SupplierSync__Enabled, Iiko__RuntimeApiCallsEnabled, verification Enabled/VerificationStatus/ContractVerificationStatus, and verification bypass flags.
Read-only smoke is an operator procedure: do not create/approve a posting. It is not implemented by disabling runtime capabilities.
New/updated Syrve connection passwords entered through Pay are stored in SecretRecords as AES-GCM encrypted local secrets and referenced from SyrveConnections.PasswordSecretRef as pay-secret:{secretRecordId}. The API never returns raw passwords or secret references in SyrveConnectionDto.
Deployment-managed secrets remain supported: set PasswordSecretRef=env:YOUR_VAR directly in the database/deployment path when the password is managed outside Pay, and define YOUR_VAR in the process environment. Legacy secret:VAR aliases are normalized to env:VAR.
Iiko runtime on Pay API host
Pay uses the minimal AddIikoConnectorForPayHost subset. Deployment must preserve transport endpoint/method configuration from repository appsettings.json when replacing the Iiko section, but no runtime/verification status or bypass flag is required.
Before every approved write, Pay still enforces scoped RBAC, manual approval, idempotency, payable/amount/allocation/anomaly checks, server applicability, credentials, HTTP/RPC and method-specific preflight, plus retry/payload/response limits. Generic RPC failures are interpreted by the method-specific preflight policy; there is no deployment bypass.
Optional operator-provided test targets:
env
IIKO_TEST_PAYMENT_ACCOUNT_FROM=<guid>
TEST_SYRVE_CASHFLOW_ARTICLE_ID=<guid>Warning: Always-on capability is not authorization. Never approve a production posting without the owner-approved production procedure and all safety checks.
Never commit real secrets.
Mistral document recognition
| Environment variable | .NET key | Default / rule |
|---|---|---|
MISTRAL_API_KEY | Mistral__ApiKey | Secret; required only when enabled. |
MISTRAL_ENABLED | Mistral__Enabled | false. |
MISTRAL_BASE_URL | Mistral__BaseUrl | https://api.mistral.ai; HTTPS is required in Production. |
MISTRAL_OCR_MODEL | Mistral__OcrModel | mistral-ocr-latest; required when enabled. |
MISTRAL_TIMEOUT_SECONDS | Mistral__TimeoutSeconds | 180, positive. |
MISTRAL_MAX_RETRIES | Mistral__MaxRetries | 3, non-negative. |
MISTRAL_DELETE_REMOTE_FILES | Mistral__DeleteRemoteFiles | true. |
MISTRAL_REMOTE_FILE_EXPIRY_SECONDS | Mistral__RemoteFileExpirySeconds | 3600, positive. |
MISTRAL_INCLUDE_BLOCKS | Mistral__IncludeBlocks | true; required for source anchoring. |
MISTRAL_CONFIDENCE_GRANULARITY | Mistral__ConfidenceGranularity | word; word/page. |
MISTRAL_TABLE_FORMAT | Mistral__TableFormat | markdown; markdown/html. |
RECOGNITION_WORKER_ENABLED | RecognitionWorker__Enabled | true. |
RECOGNITION_WORKER_POLL_INTERVAL_SECONDS | RecognitionWorker__PollIntervalSeconds | 5. |
RECOGNITION_WORKER_LEASE_SECONDS | RecognitionWorker__LeaseSeconds | 300. |
RECOGNITION_WORKER_BATCH_SIZE | RecognitionWorker__BatchSize | 5. |
RECOGNITION_LEARNING_ENABLED | RecognitionLearning__Enabled | true. |
RECOGNITION_LEARNING_REQUIRE_MANUAL_APPROVAL | RecognitionLearning__RequireManualApproval | true. |
RECOGNITION_LEARNING_MINIMUM_APPROVED_EXAMPLES | RecognitionLearning__MinimumApprovedExamples | 500. |
RECOGNITION_LEARNING_MINIMUM_EXAMPLES_PER_BANK | RecognitionLearning__MinimumExamplesPerBank | 50. |
RECOGNITION_LEARNING_DATASET_BUILD_INTERVAL_HOURS | RecognitionLearning__DatasetBuildIntervalHours | 168. |
RECOGNITION_LEARNING_CANARY_PERCENT | RecognitionLearning__CanaryPercent | 5. |
BankFiles__RecognitionReviewRetentionHours | same | 168. |
Startup validation allows a missing key/model only while disabled. Health checks make no paid request. The key is runtime environment configuration only: never a build arg, image layer, appsettings value, database value, API/browser value or log field.