Μετάβαση στο περιεχόμενο

Error handling

Όλα τα errors επιστρέφονται σε RFC 7807 ProblemDetails format. Ένα μοτίβο για logging, monitoring και troubleshooting — χωρίς ξεχωριστή λογική ανά provider.

Error response format

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  "title": "Not Found",
  "status": 404,
  "detail": "Application APP-12345 not found.",
  "instance": "/allianz/applications/APP-12345",
  "correlationId": "9f3a2b1c-4d5e-6789-abcd-ef0123456789"
}
Field Τι είναι
type URI για την κατηγορία του error
title Σύντομη περιγραφή
status HTTP status
detail Αναλυτικό μήνυμα (χωρίς ευαίσθητα δεδομένα)
instance Path του request
correlationId Trace ID — στείλτε το στο support

Status codes

4xx — fix το request

Status Σημασία Common causes
400 Bad Request Δεν έγινε parse Invalid JSON, wrong types
401 Unauthorized Auth failed Missing/expired/invalid token
403 Forbidden Authenticated, no permission Missing role/scope
404 Not Found Δεν υπάρχει route/resource Λάθος URL ή unknown ID
409 Conflict State mismatch Duplicate submission
422 Unprocessable Entity Business validation failed Provider rules
429 Too Many Requests Rate limit Δείτε Rate limits

5xx — retry με προσοχή

Status Σημασία Τι να κάνετε
500 Internal Server Error Unexpected exception Retry με backoff
502 Bad Gateway Upstream provider error Συνήθως αξίζει retry
503 Service Unavailable Service down ή circuit breaker Exponential backoff
504 Gateway Timeout Upstream timeout Retry, escalate αν επιμένει

Provider business errors

Requests που είναι τεχνικά σωστά αλλά απορρίπτονται για business λόγους από τον provider → 422 με extra info:

{
  "type": "https://docs.insurancegateway.gr/errors/provider-business",
  "title": "Provider business error",
  "status": 422,
  "detail": "Allianz declined: vehicle has active policy.",
  "instance": "/allianz/contracts/auto-calculate",
  "provider": "Allianz",
  "providerErrorMessage": "Vehicle ABC-1234 already has an active policy.",
  "providerDetailedMessage": "Active policy P-998877 expires 2026-12-31.",
  "category": "business",
  "correlationId": "9f3a2b1c-..."
}

Upstream timeout (504)

Όταν ο upstream provider δεν απαντήσει εντός του timeout, ή το request ακυρώθηκε από τον client:

{
  "type": "https://docs.insurancegateway.gr/errors/upstream-timeout",
  "title": "Upstream provider timeout",
  "status": 504,
  "detail": "A task was canceled.",
  "instance": "/orizon/quotations/calculate",
  "provider": "Orizon",
  "category": "timeout",
  "correlationId": "9f3a2b1c-..."
}

Τι βλέπει ο consumer vs τι είναι κρυμμένο

Όχι όλα τα upstream errors εκτίθενται με τον ίδιο τρόπο. Το API κάνει σαφή διάκριση μεταξύ business errors (που είναι ασφαλές να επιστραφούν αυτούσια) και transport/infrastructure errors (που μπορεί να αποκαλύψουν εσωτερικές λεπτομέρειες — credentials, internal URLs, stack traces).

422 — business: πλήρες upstream message

Το providerErrorMessage και το detail περιέχουν αυτούσιο το upstream μήνυμα. Είναι actionable για end-user feedback (πχ "ο ΑΦΜ είναι λάθος", "το όχημα έχει ήδη ενεργό συμβόλαιο").

{
  "status": 422,
  "title": "Provider business error",
  "detail": "Interamerican declined: ΑΦΜ 123456789 δεν είναι έγκυρος.",
  "provider": "Interamerican",
  "providerErrorMessage": "ΑΦΜ 123456789 δεν είναι έγκυρος.",
  "category": "business",
  "correlationId": "9f3a2b1c-..."
}

502 — transport: μόνο summary, όχι raw body

Το upstream body ΔΕΝ εκτίθεται. Επιστρέφονται μόνο provider και providerStatusCode ως summary. Το raw payload (που μπορεί να περιέχει expired API keys, internal endpoints, SOAP envelopes με credentials) πάει μόνο σε server-side logs με redaction.

{
  "status": 502,
  "title": "Upstream provider error",
  "detail": "Upstream provider returned an error.",
  "provider": "Allianz",
  "providerStatusCode": 401,
  "category": "transport",
  "correlationId": "9f3a2b1c-..."
}

Παράδειγμα: ο upstream provider επέστρεψε 401 Unauthorized με body {"error":"Invalid API Key 'sk_live_abc123...'"}. Ο consumer βλέπει μόνο providerStatusCode: 401 — όχι το API key, όχι το raw error message.

504 — timeout: γενικό μήνυμα

Γενικό "timed out" χωρίς υποδείξεις για το τι έκανε ο upstream εκείνη τη στιγμή. Καμία λεπτομέρεια για internal retry mechanics ή τοπολογία.

Πώς να το παρουσιάσετε στον end-user

  • 422: εκθέστε το providerErrorMessage αυτούσιο — είναι actionable ("Διορθώστε τον ΑΦΜ", "Το όχημα έχει ήδη ασφάλεια").
  • 502 / 504: εμφανίστε generic message ("Υπηρεσία προσωρινά μη διαθέσιμη — δοκιμάστε ξανά σε λίγο") + δείξτε το correlationId / traceId για support escalation. Μην προσπαθήσετε να ερμηνεύσετε το providerStatusCode στον τελικό χρήστη.

Error envelope extensions

Πέρα από τα standard RFC 7807 fields, η απάντηση περιέχει extra info ανάλογα με το είδος του error. Όλα μπαίνουν flat στο top-level object (όχι nested).

Extension key Σε ποιο status εμφανίζεται Τι περιέχει
category όλα business | transport | timeout | unexpected — μια λέξη για routing σε metrics/alerts
provider 422, 502, 504 Όνομα του upstream provider (Allianz, Orizon, Totalware, ErgoHellas, ...)
providerErrorMessage 422 Human-readable μήνυμα από τον provider — συνήθως translate-ready
providerDetailedMessage 422 Συμπληρωματικές πληροφορίες όταν τις παρέχει ο provider
providerStatusCode 502 Το upstream HTTP status (όταν ο provider είναι REST και έχουμε numeric status)
traceId όλα Server-side trace identifier
correlationId όλα Alias του traceId για legacy clients

Πώς να τα χρησιμοποιείτε

  • Logging/monitoring: category + provider δίνουν αρκετό grouping για dashboards.
  • End-user messages: providerErrorMessage είναι το safest — μην εκθέτετε detail raw (μπορεί να περιέχει υποδομή λεπτομέρειες).
  • Retry decisions: βασιστείτε μόνο στο status. Το providerStatusCode είναι για observability, όχι για logic.

correlationId — γιατί έχει σημασία

Κάθε request έχει correlationId. Το χρησιμοποιούμε στα API logs και στις κλήσεις προς upstream providers — ο πιο γρήγορος τρόπος να μιλάμε για το ίδιο incident.

Επιστρέφεται και στο response header X-Correlation-ID.

Έχετε δικό σας request tracing; Στείλτε X-Correlation-ID και το προωθούμε:

curl -H "X-Correlation-ID: my-trace-id-123" \
     -H "Authorization: Bearer $TOKEN" \
     https://api.insurancegateway.gr/...

Retry strategy με code samples

Για 429, 502, 503, 504 η σωστή στρατηγική είναι exponential backoff με jitter. Παρακάτω συγκεκριμένα νούμερα και έτοιμα snippets σε C#, Node.js και Python.

Backoff parameters

Parameter Τιμή Σχόλιο
Initial delay 1s Πρώτο retry μετά από 1 δευτερόλεπτο
Multiplier 2x Exponential growth (1s → 2s → 4s → 8s → 16s)
Max attempts 5 Μετά από αυτό, escalate / fail
Jitter ±25% Random offset για αποφυγή thundering herd
Max delay cap 30s Πάνω από αυτό δεν περιμένουμε άλλο

C# — Polly

var policy = Policy
    .HandleResult<HttpResponseMessage>(r =>
        r.StatusCode == HttpStatusCode.BadGateway ||
        r.StatusCode == HttpStatusCode.GatewayTimeout ||
        r.StatusCode == HttpStatusCode.TooManyRequests)
    .WaitAndRetryAsync(
        retryCount: 5,
        sleepDurationProvider: attempt =>
            TimeSpan.FromSeconds(Math.Min(30, Math.Pow(2, attempt)))
            + TimeSpan.FromMilliseconds(Random.Shared.Next(-250, 250)));

Node.js — axios-retry

axiosRetry(axios, {
  retries: 5,
  retryDelay: (retryCount) => {
    const delay = Math.min(30000, Math.pow(2, retryCount) * 1000);
    const jitter = Math.random() * 500 - 250;
    return delay + jitter;
  },
  retryCondition: (error) => {
    const status = error.response?.status;
    return status === 502 || status === 504 || status === 429;
  }
});

Python — tenacity

from tenacity import retry, stop_after_attempt, wait_exponential_jitter, retry_if_exception

@retry(
    stop=stop_after_attempt(5),
    wait=wait_exponential_jitter(initial=1, max=30, jitter=0.25),
    retry=retry_if_exception(lambda e: e.response.status_code in (502, 504, 429))
)
def call_estia(...): ...

POST/PATCH idempotency — check-before-retry

Για mutating calls (POST/PATCH) που πήραν 502/504, μην κάνετε blind retry. Πριν ξανακάνετε write, κάντε GET με το external reference (πχ quote ID, external request ID) για να ελέγξετε αν το upstream έχει ήδη δημιουργήσει την entity. Αν ναι, χειριστείτε το ως success — μη retry.

// Pseudocode: check-before-retry για POST που πήρε 502/504
var existing = await api.GetQuoteByExternalRefAsync(externalRef);
if (existing is not null)
    return existing;                       // already created upstream — success
return await api.CreateQuoteAsync(payload); // safe to retry

Retry guide

Status Retry? Σχόλιο
400, 401, 403, 404, 422 No Διορθώστε request ή credentials
409 Maybe Δείτε αν έχει ήδη ολοκληρωθεί
429 Yes Σεβαστείτε το Retry-After, exponential backoff
500 Limited Λίγα attempts με spacing
502, 503, 504 Yes Idempotent calls ή careful retries

Mutating requests

Μην κάνετε blind auto-retry σε POST/PATCH που αλλάζουν state. Αν το request είχε partial success upstream, retry μπορεί να φέρει duplicates ή inconsistency.