Appearance
Conventions
Rules that hold for every endpoint. The API reference gives the exact types and limits of each field.
Requests and responses
- Base URLs:
https://partners.sandbox.finto.ltandhttps://partners.finto.lt. Every path starts with/v1. - JSON in UTF-8. Send
Content-Type: application/json;application/json; charset=utf-8is accepted too. Uploads usemultipart/form-data. - Field names are camelCase, enum values snake_case.
- Requests are strict: an unknown field is rejected with
422 validation_failedand the field's path, so a typo cannot slip through. An optional field may be left out or sent asnull; an empty list counts as not sent. - Responses are complete: every documented field is present, and an empty value is
null. Ignore fields you do not know: new ones are added without a new version. - Enum values you do not know must not break you: new products, income types, document types, closing reasons, event types and error codes may be added (announced 30 days ahead, see the changelog). Treat an unknown product as one you do not offer, an unknown income type as
other, an unknown event type by ignoring it, and an unknown error code by its HTTP status.
Values
| Kind | Format | Example |
|---|---|---|
| Money | Euros as a JSON number, up to 2 decimals | 14500, 267.89 |
| Percentage | A number of percent, 0 to 200, up to 3 decimals | 10.78 (10.78 %) |
| Timestamp | RFC 3339. Finto sends UTC; you may send any offset | 2026-11-20T08:14:03Z, 2026-11-24T13:02:00+02:00 |
| Date | YYYY-MM-DD, Europe/Vilnius calendar | 2026-12-04 |
| Id | Opaque string up to 64 characters. Store it, do not parse it | cmi7q2x4k0001lb08h3k2v9de |
| Reference | Human-readable application number, random, not sequential | P-7KQ2MX |
Text you send must not contain control characters (except line breaks in message) or invisible formatting characters such as bidirectional overrides. Text shown to the client (offer.conditions, requirements[].description) must be in plain Lithuanian and must not contain links: give the client your link through offer.continueUrl only.
Authentication
http
Authorization: Bearer fnt_live_...- The key decides the lender and the environment:
fnt_test_keys work only in the sandbox,fnt_live_keys only in live. - In live, every request must also come from an address on your IP allow-list.
- Keys expire after at most 12 months (
GET /v1/meshowsexpiresAt); Finto reminds your technical contact 30 days before. Up to two keys are active at once, so you can rotate without downtime. POST /v1/api-key/revokerevokes the key that sends it, immediately. See security.md.
Idempotency
Every POST that changes something is safe to retry if you send an Idempotency-Key header. It is required for decisions and uploads, and optional for test events and sandbox calls.
| Rule | |
|---|---|
| What a key means | One intended change. Generate it (a UUID) when you decide to make the change, store it with the change in your system, and reuse it on every retry of that change |
| Scope | Your lender and environment. A key used once cannot be used for another application, another endpoint or another body |
| Same key, same request | You get the original result back: the same body with status 200 instead of 201 |
| Same key, different request | 422 idempotency_key_reused |
| Same key while the first request is still running | 409 idempotency_key_in_progress: wait a second and retry |
| Errors | Not stored. After a 4xx or 5xx you may retry with the same key, with or without fixing the request |
| How bodies are compared | As JSON values: field order and whitespace do not matter. For uploads: the type, title and the file's SHA-256 |
| How long keys are kept | Decisions: as long as the decision exists. Other requests: 24 hours |
Do not derive keys from the meaning of a request alone (for example <applicationId>-in_review): you may legitimately send in_review twice, and the second one would silently return the first.
Versions of an application
version counts changes you can see, starting at 1: corrected data and documents added for you. Other lenders have their own count; your own uploads do not change it. Every decision you send states the applicationVersion it is based on: the version of the application you fetched.
Order of decisions
Finto applies your decisions in the order it receives them, one at a time per application. decidedAt is for display only. If several of your processes may decide on the same application, send previousDecisionId (the decision.id you last saw, null for the first): when it no longer matches, Finto refuses the request with 409 decision_conflict instead of applying a decision made on outdated information.
Lists and cursors
GET /v1/applicationspages withlimit(up to 100) andcursor(nextCursorof the previous page).GET /v1/eventsreads after asequence(after) and returnslastSequenceto store. See webhooks.md.
Limits
| Limit | Value |
|---|---|
| Requests per key | 600 per minute (429 rate_limited with Retry-After) |
| JSON body | 64 KB |
| Upload | 10 MB per PDF; 20 files and 50 MB per application |
| Decisions | 50 per application |
| Test events | 5 per minute, not retried |
| Sandbox applications | 200 per day |
Going over a count limit returns 422 quota_exceeded; going over a rate (requests or test events per minute) returns 429 rate_limited.
Request ids
Every response has a Finto-Request-Id header (also requestId in error bodies). Log it with your request, and quote it when you contact Finto.
Code generation
Generate clients from openapi-3.0.json (OpenAPI 3.0.3). It describes the same API as openapi.json (OpenAPI 3.1, used for the documentation), with two differences that matter for code generators:
- nullable fields use
nullable: true, and files useformat: binary; - enums that Finto sends and may extend are plain strings with the known values in
x-extensible-enum, so a new value cannot make your client throw. Enums you send (DecisionStatus, reasons, requirement types) stay closed.
If you generate from openapi.json with openapi-generator instead, set enumUnknownDefaultCase=true.