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 — μην εκθέτετεdetailraw (μπορεί να περιέχει υποδομή λεπτομέρειες). - 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.