# Session totals over time `POST /analytics/v1/traffic/series` How the whole site does over time. ## Request example ```bash curl --request POST \ --url 'https://api.eu1.luigisbox.ai/analytics/v1/traffic/series?channel_ids=' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "attribution_model": "last_touch", "date_range": { "date_from": "2026-04-15T12:00:00Z", "date_to": "2026-04-15T12:00:00Z" }, "filters": { "$and": [ {} ], "$not": {}, "$or": [ {} ] }, "granularity": "hour", "group_by": [], "metrics": [ "users" ], "timezone": "UTC" }' ``` ## Query Parameters | Name | Type | Required | Description | Constraints | |---|---|---|---|---| | `channel_ids` | `Array` | Yes | One or more channel ids to aggregate over. Provide as repeated query parameters (``?channel_ids=A&channel_ids=B``). | >= 1 items | ## Request Body **Required.** ### application/json Schema: `object` #### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `attribution_model` | `string` | No | Which of the lists that showed an item is credited with an interaction on it; only `last_touch`, the last of them, is recorded today. | allowed values: last_touch | | `date_range` | `object` | Yes | The time range of the report. | — | | `date_range.date_from` | `string` | Yes | Start, inclusive. | format: date-time | | `date_range.date_to` | `string` | Yes | End, exclusive. | format: date-time | | `filters` | `anyOf(oneOf(object, object), null)` | No | Which rows are counted; every row when left out. `touchpoint` holds on a session shown a list at that touchpoint. A recommender list records no touchpoint yet, so no condition selects the sessions shown one. At most 100 conditions, in groups nested at most 10 deep — conditions are counted and the groups holding them are not. A tree past either is a `422` of type `filter_too_complex`. | — | | `filters.$and` | `Array` | No | Nodes that must all match. | >= 1 items | | `filters.$not` | `oneOf(object, object)` | No | A node that must not match. | — | | `filters.$or` | `Array` | No | Nodes of which at least one must match. | >= 1 items | | `filters.consent_granted` | `object` | No | Operators on one true-or-false field. | — | | `filters.consent_granted.eq` | `boolean` | No | Equal to. | — | | `filters.device` | `object` | No | Operators on one text field. | — | | `filters.device.eq` | `string` | No | Equal to. | — | | `filters.device.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.device.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.device.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.device.neq` | `string` | No | Not equal to. | — | | `filters.device.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.external_ab_test_variant` | `object` | No | Operators on one text field. | — | | `filters.external_ab_test_variant.eq` | `string` | No | Equal to. | — | | `filters.external_ab_test_variant.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.external_ab_test_variant.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.external_ab_test_variant.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.external_ab_test_variant.neq` | `string` | No | Not equal to. | — | | `filters.external_ab_test_variant.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.internal_ab_test_id` | `object` | No | Operators on one text field. | — | | `filters.internal_ab_test_id.eq` | `string` | No | Equal to. | — | | `filters.internal_ab_test_id.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.internal_ab_test_id.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.internal_ab_test_id.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.internal_ab_test_id.neq` | `string` | No | Not equal to. | — | | `filters.internal_ab_test_id.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.internal_ab_test_variant` | `object` | No | Operators on one text field. | — | | `filters.internal_ab_test_variant.eq` | `string` | No | Equal to. | — | | `filters.internal_ab_test_variant.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.internal_ab_test_variant.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.internal_ab_test_variant.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.internal_ab_test_variant.neq` | `string` | No | Not equal to. | — | | `filters.internal_ab_test_variant.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.landing_page` | `object` | No | Operators on one text field. | — | | `filters.landing_page.eq` | `string` | No | Equal to. | — | | `filters.landing_page.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.landing_page.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.landing_page.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.landing_page.neq` | `string` | No | Not equal to. | — | | `filters.landing_page.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.landing_page_host` | `object` | No | Operators on one text field. | — | | `filters.landing_page_host.eq` | `string` | No | Equal to. | — | | `filters.landing_page_host.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.landing_page_host.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.landing_page_host.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.landing_page_host.neq` | `string` | No | Not equal to. | — | | `filters.landing_page_host.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.personalized` | `object` | No | Operators on one true-or-false field. | — | | `filters.personalized.eq` | `boolean` | No | Equal to. | — | | `filters.platform` | `object` | No | Operators on one text field. | — | | `filters.platform.eq` | `string` | No | Equal to. | — | | `filters.platform.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.platform.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.platform.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.platform.neq` | `string` | No | Not equal to. | — | | `filters.platform.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.returning` | `object` | No | Operators on one true-or-false field. | — | | `filters.returning.eq` | `boolean` | No | Equal to. | — | | `filters.session_rank` | `object` | No | Operators on one whole-number field. | — | | `filters.session_rank.eq` | `integer` | No | Equal to. | — | | `filters.session_rank.gt` | `integer` | No | Greater than. | — | | `filters.session_rank.gte` | `integer` | No | At least. | — | | `filters.session_rank.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.session_rank.lt` | `integer` | No | Less than. | — | | `filters.session_rank.lte` | `integer` | No | At most. | — | | `filters.session_rank.neq` | `integer` | No | Not equal to. | — | | `filters.session_rank.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.touchpoint` | `object` | No | Operators on where a session was shown lists. | — | | `filters.touchpoint.eq` | `string` | No | Equal to. | allowed values: autocomplete, serp, plp, chat | | `filters.touchpoint.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.touchpoint.neq` | `string` | No | Not equal to. | allowed values: autocomplete, serp, plp, chat | | `filters.touchpoint.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.traffic_source` | `object` | No | Operators on how a session arrived. | — | | `filters.traffic_source.eq` | `string` | No | Equal to. | allowed values: direct, paid, referral, llms | | `filters.traffic_source.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.traffic_source.neq` | `string` | No | Not equal to. | allowed values: direct, paid, referral, llms | | `filters.traffic_source.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.traffic_source_type` | `object` | No | Operators on one text field. | — | | `filters.traffic_source_type.eq` | `string` | No | Equal to. | — | | `filters.traffic_source_type.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.traffic_source_type.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.traffic_source_type.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.traffic_source_type.neq` | `string` | No | Not equal to. | — | | `filters.traffic_source_type.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `filters.*` | `object` | No | Operators on one text field. | — | | `filters.*.eq` | `string` | No | Equal to. | — | | `filters.*.exists` | `boolean` | No | Has a value (`true`) or has none (`false`). | — | | `filters.*.in` | `Array` | No | One of. | >= 1 items; <= 100 items | | `filters.*.matches` | `string` | No | Contains this text. | >= 1 characters | | `filters.*.neq` | `string` | No | Not equal to. | — | | `filters.*.not_in` | `Array` | No | None of. | >= 1 items; <= 100 items | | `granularity` | `string` | No | The length of a bucket. | allowed values: hour, day, week, month, quarter, year | | `group_by` | `Array` | No | Fields that split every row, summary and series point, e.g. `["device"]`. At most one of `landing_page`, `url_path`, `url_params.*` and `context.*`; a summary or series holds at most 500 groups, and a session with several values of a key counts once in each of its groups. | <= 2 items | | `metrics` | `Array` | No | Metrics to compute; every metric of the report when left out. | >= 1 items | | `timezone` | `string` | No | The IANA timezone whose calendar the buckets follow, e.g. `Europe/Bratislava`: `UTC` or an `/` name. Every bucket after the first starts at a whole hour, or at the local midnight that starts a day, a Monday, or a month, quarter or year there, so across a change of the zone's offset a `day` lasts 23 or 25 hours. `date_range` stays two instants: give it local midnights, e.g. `2026-10-01T00:00:00+02:00`, for whole first and last buckets. | <= 64 characters | #### Request body example ```json { "attribution_model": "last_touch", "date_range": { "date_from": "2026-04-15T12:00:00Z", "date_to": "2026-04-15T12:00:00Z" }, "filters": { "$and": [ {} ], "$not": {}, "$or": [ {} ] }, "granularity": "hour", "group_by": [], "metrics": [ "users" ], "timezone": "UTC" } ``` ## Responses | Status | Description | Content | |---|---|---| | `200` | Successful Response | application/json: object | | `400` | Malformed request | application/json: object | | `401` | Missing or invalid credentials | application/json: object | | `403` | Authenticated but not permitted | application/json: object | | `422` | Request validation failed, or the report cannot answer it as asked (`exception_details.problems`). | application/json: object | | `503` | Service temporarily unavailable | application/json: object | ### 200 Successful Response #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `granularity` | `string` | Yes | The length of a bucket, as requested. | allowed values: hour, day, week, month, quarter, year | | `points` | `Array` | Yes | Every bucket of `date_range` in time order; with `group_by`, every bucket per group, by group value and then time. A bucket without data has counts and money of 0, and every other metric `null`. At most 10,000 points. | — | | `points[].group` | `object` | No | Its `group_by` values; present with `group_by`. | — | | `points[].group.channel_id` | `anyOf(string, null)` | No | The channel the counts come from. | — | | `points[].group.consent_granted` | `anyOf(boolean, null)` | No | The visitor granted personalization consent. | — | | `points[].group.context` | `anyOf(object, null)` | No | Context values grouped as `context.`, keyed by name. | — | | `points[].group.context.*` | `anyOf(string, null)` | No | Additional property. | — | | `points[].group.device` | `anyOf(string, null)` | No | Device: `desktop`, `mobile`, `tablet`, … | — | | `points[].group.external_ab_test_variant` | `anyOf(string, null)` | No | Variant of the client's own A/B test. | — | | `points[].group.internal_ab_test_id` | `anyOf(string, null)` | No | Luigi's Box A/B test. | — | | `points[].group.internal_ab_test_variant` | `anyOf(string, null)` | No | Variant of the Luigi's Box A/B test. | — | | `points[].group.landing_page` | `anyOf(string, null)` | No | The entry URL. | — | | `points[].group.landing_page_host` | `anyOf(string, null)` | No | The entry URL's host. | — | | `points[].group.personalized` | `anyOf(boolean, null)` | No | The session was served personalized results. | — | | `points[].group.platform` | `anyOf(string, null)` | No | Platform: `ios`, `android`, `windows`, … | — | | `points[].group.result_type` | `anyOf(string, null)` | No | Type of the result: `item`, `category`, `brand`, … | — | | `points[].group.returning` | `anyOf(boolean, null)` | No | The visitor had an earlier session: `session_rank` of 2 or more. | — | | `points[].group.surface_id` | `anyOf(string, null)` | No | The Surface that answered. | — | | `points[].group.touchpoint` | `anyOf(string, null)` | No | Where the lists were shown. | — | | `points[].group.traffic_source` | `anyOf(string, null)` | No | How the session arrived. | — | | `points[].group.traffic_source_type` | `anyOf(string, null)` | No | Traffic source type, e.g. `chatgpt`. | — | | `points[].group.url_host` | `anyOf(string, null)` | No | Host of the page the search ran on, e.g. a language site. | — | | `points[].group.url_params` | `anyOf(object, null)` | No | URL parameters grouped as `url_params.`, keyed by name, so `url_params.utm_source` reads as `group.url_params.utm_source`. | — | | `points[].group.url_params.*` | `anyOf(string, null)` | No | Additional property. | — | | `points[].group.url_path` | `anyOf(string, null)` | No | Path of the page the search ran on, without its query string. | — | | `points[].metrics` | `object` | Yes | Metrics over the bucket. | — | | `points[].metrics.attributed_revenue` | `anyOf(number, null)` | No | Order value credited to Luigi's Box lists. | — | | `points[].metrics.average_order_value` | `anyOf(number, null)` | No | Revenue / transactions. | — | | `points[].metrics.engaged_sessions` | `anyOf(integer, null)` | No | Sessions with a click on a result. | — | | `points[].metrics.engagement_rate` | `anyOf(number, null)` | No | Engaged sessions / sessions. | — | | `points[].metrics.pageviews` | `anyOf(integer, null)` | No | Pages viewed. | — | | `points[].metrics.revenue` | `anyOf(number, null)` | No | Order value of every transaction in the sessions. | — | | `points[].metrics.revenue_per_session` | `anyOf(number, null)` | No | Revenue / sessions. | — | | `points[].metrics.revenue_share` | `anyOf(number, null)` | No | Revenue / revenue of the whole report; `null` when the report has no revenue. | — | | `points[].metrics.session_share` | `anyOf(number, null)` | No | Sessions / sessions of the whole report. | — | | `points[].metrics.sessions` | `anyOf(integer, null)` | No | Sessions of the site; a session with a single event is not counted. | — | | `points[].metrics.sessions_transaction_rate` | `anyOf(number, null)` | No | Sessions with a transaction / sessions. | — | | `points[].metrics.sessions_with_transactions` | `anyOf(integer, null)` | No | Sessions with a transaction. | — | | `points[].metrics.transactions` | `anyOf(integer, null)` | No | All transactions in the sessions, credited to a service or not. | — | | `points[].metrics.users` | `anyOf(integer, null)` | No | Unique users. | — | | `points[].starts_at` | `string` | Yes | Start of the bucket, as a UTC instant at a boundary of `timezone`; the first and last buckets are clipped to `date_range`. | format: date-time | | `timezone` | `string` | Yes | The timezone the buckets follow: the request's `timezone`, or `UTC`. | — | ##### Response example ```json { "granularity": "hour", "points": [ { "group": { "channel_id": "example", "consent_granted": true, "context": { "additionalProperty": "example" }, "device": "example", "external_ab_test_variant": "example", "internal_ab_test_id": "example", "internal_ab_test_variant": "example", "landing_page": "example", "landing_page_host": "example", "personalized": true, "platform": "example", "result_type": "example", "returning": true, "surface_id": "example", "touchpoint": "autocomplete", "traffic_source": "direct", "traffic_source_type": "example", "url_host": "example", "url_params": { "additionalProperty": "example" }, "url_path": "example" }, "metrics": { "attributed_revenue": 1, "average_order_value": 1, "engaged_sessions": 1, "engagement_rate": 1, "pageviews": 1, "revenue": 1, "revenue_per_session": 1, "revenue_share": 1, "session_share": 1, "sessions": 1, "sessions_transaction_rate": 1, "sessions_with_transactions": 1, "transactions": 1, "users": 1 }, "starts_at": "2026-04-15T12:00:00Z" } ], "timezone": "example" } ``` ### 400 Malformed request #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 401 Missing or invalid credentials #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 403 Authenticated but not permitted #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 422 Request validation failed, or the report cannot answer it as asked (`exception_details.problems`). #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | Yes | Machine-readable error context. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.problems` | `Array` | No | Why the report cannot answer the request as asked; `validation_errors` covers malformed fields. | — | | `exception_details.problems[].code` | `string` | Yes | Stable code of the problem; switch on it, not on `message`. | allowed values: too_many_groups, too_many_high_cardinality_groups, too_many_points, ab_test_variant_without_test, nothing_to_compare, report_too_large | | `exception_details.problems[].loc` | `Array` | Yes | Where in the request, as in `validation_errors[].loc`, e.g. `["body", "group_by", 1]`. | — | | `exception_details.problems[].message` | `string` | Yes | What to change. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "problems": [ { "code": "too_many_groups", "loc": [ "example" ], "message": "example" } ], "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 503 Service temporarily unavailable #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ```