Errors

The error shape, every error type, and which errors to retry.

A failed request returns an HTTP error status and a JSON body:

{
  "error": {
    "type": "invalid_request",
    "message": "duplicate option text: ' Yes'",
    "param": "questions.q.options"
  }
}
FieldDescription
typeOne of the types below. Branch on this
messageA description for people. It can change, so don't parse it
paramThe dotted path to the field at fault, such as questions.risk.levels, or null

Validation stops at the first problem, so a request with several problems reports one at a time.

Error types

HTTPtypeWhenRetry
400invalid_requestMalformed JSON, or the request breaks a rule in Create a decisionNo. Fix the request
401authentication_errorMissing or invalid API key, including a revoked keyNo. Check the key
402insufficient_creditThe organization's credit balance is used upNo. An admin adds credit in the portal under Settings → Billing
404model_not_foundUnknown modelNo. Use grayson-1
404invalid_requestAny path or method other than POST /v1/decideNo. Fix the URL
413request_too_largeOver 32,768 tokens, or a body over 2 MBNo. Shorten the state or ask fewer questions
429rate_limitedToo many requestsYes, with backoff
500internal_errorSomething failed on Finic's sideYes
503overloadedTemporarily at capacity, or the decision didn't finish within 30 secondsYes, with backoff

An organization that is out of credit gets 402 before its request is checked, so fix the credit first.

During a deployment or an outage you may also get a 502 or 504 from the network edge. These come without a JSON body or an X-Request-Id header; treat them like 503.

Invalid requests

Some invalid_request errors and the param they point to:

Causeparammessage
A state of only whitespace, such as " "statestate must not be blank
Question name starts with a digitquestions.1stinvalid name '1st': must match ^[A-Za-z_][A-Za-z0-9_]{0,63}$
Two options whose descriptions differ only in case or whitespacequestions.q.optionsduplicate option text: ' Yes'
Score values not strictly increasingquestions.risk.valuesvalues must be strictly increasing (levels are lowest first)
options on a noulquestions.q.optionsExtra inputs are not permitted
An unknown top-level field, such as temperaturenullUnknown field "temperature".
An unknown field inside a questionquestions.q.temperatureExtra inputs are not permitted

Retrying

A decision changes nothing on Finic's side except usage, so any request is safe to retry. Each successful response uses credit, so a retry after a timeout whose request did succeed is charged twice.

  • Retry 429, 500, 502, 503 and 504, and network errors and timeouts, with exponential backoff and jitter.
  • Don't retry other 4xx errors without changing the request; they will fail the same way.
  • Set your client timeout above 30 seconds, the longest a decision can run.
import random
import time

import requests

RETRYABLE = {429, 500, 502, 503, 504}


def decide(body: dict, api_key: str, attempts: int = 5) -> dict:
    for attempt in range(attempts):
        last = attempt == attempts - 1
        try:
            response = requests.post(
                "https://api.finic.ai/v1/decide",
                headers={"Authorization": f"Bearer {api_key}"},
                json=body,
                timeout=60,
            )
        except (requests.ConnectionError, requests.Timeout):
            if last:
                raise
        else:
            if response.status_code not in RETRYABLE or last:
                break
        time.sleep(min(8.0, 0.5 * 2**attempt) * random.uniform(0.5, 1.0))

    if not response.ok:
        request_id = response.headers.get("X-Request-Id")
        try:
            error = response.json()["error"]
        except ValueError:  # an edge 502/504 has no JSON body
            raise RuntimeError(f"{response.status_code} (request {request_id})")
        raise RuntimeError(f"{response.status_code} {error['type']}: {error['message']} (request {request_id})")
    return response.json()

Contact support

Email support@finic.ai with the value of the X-Request-Id header. Every response from the API has one, errors included.

Finic records request metadata only, so a request id lets support see the time, key, model, status and error type of a request, but not its state, questions or answers. If a problem depends on the content, describe it or send a redacted example.

On this page