Appearance
Errors
Every error is an application/problem+json body (RFC 9457) with a stable machine-readable code:
json
{
"type": "https://partners.finto.lt/docs/errors#validation_failed",
"title": "Request validation failed",
"status": 422,
"code": "validation_failed",
"detail": "2 fields are invalid.",
"requestId": "req_8c1f0e6b2a",
"errors": [
{ "path": "offer.aprc", "message": "Required" },
{ "path": "offer.validUntil", "message": "Must not be in the past" }
]
}- Branch on
code, not ontitleordetail(those are for people and may be reworded). New codes may be added: treat an unknown one by its HTTP status. errorslists field problems forvalidation_failed; otherwise it isnull.pathuses dots and indexes:requirements.0.description.requestIdis also in theFinto-Request-Idheader of every response, successful or not. Log it, and quote it when you contact Finto.- For an application or document that is not yours, the answer is always the same
404, whatever else is wrong with the request.
When to retry
| Status | Retry? |
|---|---|
400, 401, 403, 404, 410, 413, 415, 422 | No: fix the request, the key, or your state |
409 | Depends on the code: idempotency_key_in_progress yes, after a second; decision_conflict after fetching the application and deciding again; the others no |
429 | Yes, after Retry-After seconds |
500, 502, 503, 504, timeouts | Yes, with exponential backoff (for example 1, 2, 4, 8 seconds, then every minute). Repeating a POST with the same Idempotency-Key is safe |
Codes
bad_request
400. The body is not valid JSON, or the request is malformed in a way that is not about a specific field.
unauthorized
401. The Authorization header is missing, the key is unknown, expired or revoked, or it belongs to the other environment (a fnt_live_ key in the sandbox or the other way round).
ip_not_allowed
403. The request did not come from an IP address on your allow-list. detail shows the address Finto saw; send it to Finto if it is yours. In live, every request is checked against the list.
not_found
404. No such application, document or endpoint for you. An application that exists but was not sent to you also returns 404, so ids cannot be probed. The sandbox endpoints return 404 in live.
idempotency_key_required
400. The POST needs an Idempotency-Key header.
idempotency_key_reused
422. You used an Idempotency-Key again for a different request: another body, another application or another endpoint. Use a new key for a different change. (The same key with the same request is not an error: you get the original result back.)
idempotency_key_in_progress
409. A request with the same Idempotency-Key is still being processed. Wait a second and retry; you will get its result.
invalid_transition
409. The decision status does not follow your current one, for example approved after cancelled, anything but a correction or an outcome after rejected on the same version, terminated before disbursed, or cancelled after the payout with a reason other than withdrawal_right or other. detail says from what to what. See the table in lifecycle.md.
decision_conflict
409. You sent previousDecisionId, and it is not your current decision: another decision got in first. Fetch the application, look at decision, and decide again.
application_closed
409. The application is closed for you. Only contract_signed, disbursed, cancelled and terminated are accepted now. Usually a race with application.closed: stop working on the application.
application_withdrawn
410. The client withdrew consent. The application and its documents are no longer available, and no decisions are accepted. Stop processing.
application_archived
410. More than 14 days have passed since your final decision or since the application closed for you; its data is no longer served. The list still shows it without personal data, your decisions stay readable, and reports about the agreement (cancelled with withdrawal_right, terminated) are still accepted.
cursor_expired
410. Events after your after cursor were already deleted from the feed (it keeps 30 days). Reconcile with GET /v1/applications?updatedAfter=..., then read the feed without after and store the new lastSequence.
quota_exceeded
422. A count limit was reached: 50 decisions or 20 uploads (50 MB) on one application, or 200 sandbox applications per day. See conventions.md.
payload_too_large
413. JSON bodies may be up to 64 KB, uploads up to 10 MB.
unsupported_media_type
415. The Content-Type is not the expected one (application/json, or multipart/form-data for uploads), or the uploaded file is not a PDF.
validation_failed
422. The body or the query parameters do not match the schema, or the values do not fit the application. errors lists every field problem. Common messages:
message | Typical cause |
|---|---|
Required | A required field is missing (offer.aprc) |
Required when status is approved | offer missing with approved; same pattern for other statuses |
Not allowed when status is rejected | A field that belongs to another status, such as offer with rejected |
Unknown field | A field name the API does not know, for example offer.apr instead of offer.aprc; check the spelling |
Expected number | A value of the wrong type, such as "14500" in quotes |
At most 2 decimal places | A money value like 267.891 |
Must be at least amount | offer.totalAmountPayable below offer.amount |
Must not be in the past | offer.validUntil before today (Vilnius time) |
Must be at most 180 days ahead | offer.validUntil too far ahead |
Must not be in the future | decidedAt, contract.signedAt or disbursement.disbursedAt more than 5 minutes ahead of Finto's clock; termination.endedOn after today (Vilnius time) |
Must not be before the application was signed | One of those more than a day before the client signed |
Must be an https address on your registered domain, without a port, user name or IP address | offer.continueUrl |
Links are not allowed in text shown to the client | A link in offer.conditions or requirements[].description |
Contains control or formatting characters | Invisible characters in a text field |
Must not be higher than the latest version | applicationVersion above the application's version |
Unknown document: upload it to this application first | An id in offer.documentIds that is not your own upload on this application |
Must not be higher than your latest event sequence | after in GET /v1/events above your last event, for example a cursor kept from the other environment |
Other messages describe the limit that was broken, for example a value out of range or not one of the allowed enum values.
rate_limited
429. More than 600 requests per minute with this key, or more than 5 test events (POST /v1/events/test) per minute. Wait Retry-After seconds. A normal integration uses a small fraction of the limit; if you hit it, check for a loop.
internal_error
500. Something failed on Finto's side. Retry with backoff; Finto is alerted automatically. If it persists, contact Finto with the requestId.
service_unavailable
503. Finto is briefly unavailable, for example during a deployment. Retry with backoff.