{"openapi":"3.1.0","info":{"title":"Boring API","description":"Small, deterministic analysis APIs for business tables: supplier price list comparison, PDF table extraction and product feed validation. Open with a rate limit; an optional X-API-Key raises it. Answers carry source locations and a quality level. Responses are English with language-neutral codes; input headers are recognised in many languages. For agents also as MCP at /mcp; overview at /llms.txt.","version":"0.4.0"},"servers":[{"url":"https://api.relaex-engineers.de"}],"paths":{"/v1/documents/tables":{"post":{"tags":["documents"],"summary":"Extract tables from a PDF","description":"Extracts tables from a text-based PDF.\n\n**Input:** multipart `file` (PDF up to 20 MiB, 60 pages per call) and optional `options` as a JSON\nstring following `/v1/schemas/documents-tables-options`: `pages` (e.g. `\"1-3,7\"`), `strategy`\n(`lines`, `text`, `auto`), `merge_continuations`, `include_cells`, `output` (`json` or `csv`),\n`table_id` to choose the CSV table.\n\n**Behaviour:** pdfplumber line and text strategies, detection of header rows, titles and\nfootnotes, tables continued across pages are merged only with evidence (identical header row\nor column edges). Each table carries page, bounding box in PDF points (origin top left) and a\nquality level with reasons. Cells stay text. Pages without text are reported, scans as\n`OCR_REQUIRED`; nothing is guessed.\n\n**Output:** `ApiResponse` with `TablesResult`, or with `output=csv` the best table as `text/csv`\n(semicolon) with headers `X-Table-Id`, `X-Table-Page`, `X-Table-Confidence`. The CSV can be\npassed straight to `/v1/supplier/compare`, which also accepts PDFs directly\n(`options.pdf_old`, `options.pdf_new`).\n\n**Errors:** 400 not a PDF or encrypted, 413 too large or too many pages, 422 invalid options or\ntable not found, 429 rate limit, 504 timeout.\n\n**Languages:** responses are in English. Error, warning and rule codes are stable and\nlanguage-neutral; agents may translate messages for the user. Text in any language or script is extracted as embedded in the PDF.\n\n**Access:** open, rate-limited per IP and per MCP session; an `X-API-Key` header raises the\nlimit. Uploaded files are processed in memory and never stored. Also available as MCP tool\n`documents_tables` at `/mcp` (Streamable HTTP).","operationId":"documents_tables_v1_documents_tables_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_documents_tables_v1_documents_tables_post"}}}},"responses":{"200":{"description":"ApiResponse with TablesResult (JSON) or the best table as CSV","content":{"application/json":{"schema":{}}}},"400":{"description":"INVALID_PDF, ENCRYPTED_PDF"},"413":{"description":"FILE_TOO_LARGE, PAGE_LIMIT_EXCEEDED"},"422":{"description":"INVALID_OPTIONS, NO_TABLE_FOUND, TABLE_NOT_FOUND"},"429":{"description":"RATE_LIMITED"},"504":{"description":"ANALYSIS_TIMEOUT"}}}},"/v1/merchant/validate":{"post":{"tags":["merchant"],"summary":"Validate a product feed for Google, Shopify or Meta","description":"Validates a product feed against a platform profile.\n\n**Input:** multipart `file` (CSV, TSV or XLSX up to 20 MiB, 250,000 rows) and optional `options`\nas a JSON string following `/v1/schemas/merchant-validate-options`: `profile`\n(`google_merchant`, `shopify_products_csv`, `meta_catalog`; default Google), `sheet`,\n`column_overrides` (target field → column header), `max_issues` (default 5,000),\n`include_info`. The profile may also be sent as its own form field `profile`.\n\n**Behaviour:** the header row and columns are recognised via versioned alias lists; the\nprofile's rules run per row: required fields, allowed values (with the target value as hint\nfor synonyms such as `auf Lager` or `en stock`), price format (`12.99 EUR`; Shopify without\ncurrency), GTIN check digit, URLs and https, lengths, unique IDs, Shopify variant options,\nGoogle identifiers. Each issue carries rule code, severity (`error`, `warning`, `info`), row,\ncolumn, message, `fix_hint` and a value excerpt. Nothing is changed or guessed.\n\n**Output:** `ApiResponse` with `ValidateResult`: profile, file with column mapping and unchecked\ncolumns, `issues[]`, `issues_truncated`, `summary` (rows, valid rows, rows with\nerrors/warnings, counts per severity and code, `human_review_required`).\n\n**Errors:** 400 invalid file, 413 too large, 422 no header row, ambiguous column, unknown\nprofile or invalid options, 429 rate limit, 504 timeout.\n\n**Languages:** responses are in English. Error, warning and rule codes are stable and\nlanguage-neutral; agents may translate messages for the user. Column headers and common cell words are recognised in English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Czech, Turkish, Swedish, Danish, Norwegian, Finnish, Korean, Japanese and Chinese; numbers in both 1.234,56 and 1,234.56 style; any ISO 4217 currency (no conversion).\n\n**Access:** open, rate-limited per IP and per MCP session; an `X-API-Key` header raises the\nlimit. Uploaded files are processed in memory and never stored. Also available as MCP tool\n`merchant_validate` at `/mcp` (Streamable HTTP).","operationId":"merchant_validate_v1_merchant_validate_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_merchant_validate_v1_merchant_validate_post"}}}},"responses":{"200":{"description":"ApiResponse with ValidateResult","content":{"application/json":{"schema":{}}}},"400":{"description":"INVALID_FILE, EMPTY_FILE, UNSUPPORTED_FORMAT, INVALID_ENCODING"},"413":{"description":"FILE_TOO_LARGE, ROW_LIMIT_EXCEEDED"},"422":{"description":"HEADER_NOT_FOUND, COLUMN_AMBIGUOUS, SHEET_NOT_FOUND, PROFILE_NOT_FOUND, INVALID_OPTIONS"},"429":{"description":"RATE_LIMITED"},"504":{"description":"ANALYSIS_TIMEOUT"}}}},"/v1/usage/me":{"get":{"tags":["usage"],"summary":"Own usage per product (API key)","operationId":"usage_me_v1_usage_me_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Usage Me V1 Usage Me Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/usage/stats":{"get":{"tags":["usage"],"summary":"Usage statistics (admin key)","operationId":"usage_stats_v1_usage_stats_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Usage Stats V1 Usage Stats Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["meta"],"summary":"Status and versions","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}},"/v1/schemas/{name}":{"get":{"tags":["meta"],"summary":"JSON schema of a contract","operationId":"schema_v1_schemas__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Schema V1 Schemas  Name  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/supplier/compare":{"post":{"tags":["supplier"],"summary":"Compare two supplier price lists","description":"Compares an old and a new supplier price list.\n\n**Input:** multipart with `old_file` and `new_file` (CSV, XLSX, DATANORM 4/5 or text-based PDF,\neach up to 20 MiB and 250,000 rows), optional `usage_file` (SKU and annual quantity) and\n`options` as a JSON string following `/v1/schemas/supplier-compare-options`, e.g.\n`{\"default_currency\": \"EUR\", \"sheet_old\": \"Prices\", \"response_format\": \"xlsx\"}`.\n\n**Behaviour:** columns are recognised via a versioned alias list, number formats are inferred\nper column and prices normalised to unit prices. SKUs are matched in six stages (exact,\nnormalised, EAN, suffix, predecessor, secondary key). Uncertain values are reported as\n`could_not_determine`, never guessed; every row carries its source location.\n\n**Output:** `ApiResponse` with findings (status, changes, delta, risk rule, quality level,\nreview flag), summary (counts, across-the-board change, cost impact) and warnings. With\n`response_format=xlsx` an Excel report is returned.\n\n**Errors:** 400 invalid or empty file, 413 too large, 422 no header row or required column or\ninvalid options, 429 rate limit, 504 timeout.\n\n**Languages:** responses are in English. Error, warning and rule codes are stable and\nlanguage-neutral; agents may translate messages for the user. Column headers and common cell words are recognised in English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Czech, Turkish, Swedish, Danish, Norwegian, Finnish, Korean, Japanese and Chinese; numbers in both 1.234,56 and 1,234.56 style; any ISO 4217 currency (no conversion).\n\n**Access:** open, rate-limited per IP and per MCP session; an `X-API-Key` header raises the\nlimit. Uploaded files are processed in memory and never stored. Also available as MCP tool\n`supplier_compare` at `/mcp` (Streamable HTTP).","operationId":"supplier_compare_v1_supplier_compare_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_supplier_compare_v1_supplier_compare_post"}}}},"responses":{"200":{"description":"ApiResponse with CompareResult (JSON) or Excel report","content":{"application/json":{"schema":{}}}},"400":{"description":"INVALID_FILE, EMPTY_FILE, UNSUPPORTED_FORMAT, INVALID_ENCODING"},"413":{"description":"FILE_TOO_LARGE, ROW_LIMIT_EXCEEDED"},"422":{"description":"HEADER_NOT_FOUND, REQUIRED_COLUMN_NOT_FOUND, COLUMN_AMBIGUOUS, SHEET_NOT_FOUND, INVALID_OPTIONS"},"429":{"description":"RATE_LIMITED"},"504":{"description":"ANALYSIS_TIMEOUT"}}}}},"components":{"schemas":{"Body_documents_tables_v1_documents_tables_post":{"properties":{"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"},"options":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Options"},"file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Url"}},"type":"object","title":"Body_documents_tables_v1_documents_tables_post"},"Body_merchant_validate_v1_merchant_validate_post":{"properties":{"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"},"options":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Options"},"profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile"},"file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Url"}},"type":"object","title":"Body_merchant_validate_v1_merchant_validate_post"},"Body_supplier_compare_v1_supplier_compare_post":{"properties":{"old_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Old File"},"new_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"New File"},"usage_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Usage File"},"options":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Options"},"old_file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old File Url"},"new_file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New File Url"},"usage_file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usage File Url"}},"type":"object","title":"Body_supplier_compare_v1_supplier_compare_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"supplier","description":"Compares two supplier price lists (old vs. new) per SKU: the real unit price change after normalising price, currency, pack size and price basis, new and removed items, availability changes, across-the-board increases with outliers, review priority per finding and optional cost impact from an annual demand file."},{"name":"documents","description":"Extracts tables from text-based PDFs with page, bounding box and a quality level per table; detects header rows, titles and footnotes, merges tables continued across pages only with evidence; returns JSON or the best table as CSV."},{"name":"merchant","description":"Validates product feeds against Google Merchant Center, Shopify product CSV and Meta Commerce Catalog: required fields, allowed values with a hint for localized synonyms, GTIN check digit, price format, URLs, lengths, duplicate IDs, Shopify variants. Each issue names row, column, rule code and a fix hint; nothing is changed automatically."},{"name":"usage","description":"Usage counters per API key and admin statistics."},{"name":"meta","description":"Health, JSON schemas and discovery documents."}]}