{"openapi":"3.1.0","info":{"title":"SerenTec Cleaning API","description":"Clean a value, a column or a datetime string, or infer the type of a value or an array.\nEvery endpoint lives under `/clean/v1`. Every call is proxied to the cleaning fleet; this\nservice holds no database and no session state.\n\n**Authentication.** A key is optional for `/column`, `/value`, `/type` and `/types` and required\nfor `/datetime`. Send it in the `X-API-Key` header. The `api_key` query parameter exists for\nExcel `WEBSERVICE()`, which cannot set headers; a key in a URL lands in server logs and browser\nhistory, so prefer the header everywhere else. A wrong key is served as anonymous, not refused.\n\n**Tiers.** An anonymous caller is served under a per-request cap; a valid key lifts it. A request\nabove its cap is refused, never truncated: 401 for an anonymous caller (a key would lift the cap),\n413 for a keyed caller. `GET /clean/v1/info` reports both caps and the accepted entity hints.\n\n**Browsers.** CORS allows any origin, methods GET and POST, so a page or a browser-side agent may\ncall this API directly.\n","version":"0.2.0"},"paths":{"/clean/v1/health":{"get":{"summary":"Health","description":"Liveness: this process is up. Says nothing about the fleet behind it.","operationId":"health_clean_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResult"}}}}}}},"/clean/v1/info":{"get":{"summary":"Info","description":"The two caps, the accepted entity hints and the auth scheme. Read it before the\nfirst request. No key needed; no addresses, no key material.\n\nThe entity hints are read from the fleet (CleanColumn's parser registry) on every\ncall, so a parser added to the fleet appears here without a change to this service.","operationId":"info_clean_v1_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InfoResult"}}}},"503":{"description":"The cleaning fleet is unreachable; retry later"}}}},"/clean/v1/column":{"post":{"summary":"Clean Column","description":"Clean a column of values. The column's type is inferred from the values, or pinned\nby the entity hint; every value comes back as a CleanedCell in the input order.\n\nA hint the fleet refuses is reported as text with the reason in each cell's message\nand na_code 4 (warning), so an untouched value is never mistaken for a cleaned one.","operationId":"clean_column_clean_v1_column_post","parameters":[{"name":"api_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API key; prefer the X-API-Key header","title":"Api Key"},"description":"API key; prefer the X-API-Key header"},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleanRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleanResult"}}}},"401":{"description":"The request carries more values than the anonymous cap; a valid key in the X-API-Key header lifts it"},"413":{"description":"The request carries more values than the keyed cap; nothing lifts it"},"503":{"description":"The cleaning fleet is unreachable; retry later"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/clean/v1/value":{"get":{"summary":"Clean Single","description":"Clean one value. The type is inferred from the value alone, or pinned by the entity\nhint. Always JSON, so a VBA or Excel client can convert by type.\n\nA fleet fault is reported in-band as na_code 3 with the message, not as a 5xx, so a\nspreadsheet cell shows the reason rather than an error.","operationId":"clean_single_clean_v1_value_get","parameters":[{"name":"value","in":"query","required":true,"schema":{"type":"string","description":"Value to clean","title":"Value"},"description":"Value to clean"},{"name":"entity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Entity type hint, case-insensitive (e.g. \"date\", \"currency\", \"number\"); omit to auto-detect. GET /info lists the accepted values.","title":"Entity"},"description":"Entity type hint, case-insensitive (e.g. \"date\", \"currency\", \"number\"); omit to auto-detect. GET /info lists the accepted values."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reserved; ignored","title":"Format"},"description":"Reserved; ignored"},{"name":"api_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API key; prefer the X-API-Key header","title":"Api Key"},"description":"API key; prefer the X-API-Key header"},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleanedValue"}}}},"401":{"description":"The request carries more values than the anonymous cap; a valid key in the X-API-Key header lifts it"},"413":{"description":"The request carries more values than the keyed cap; nothing lifts it"},"503":{"description":"The cleaning fleet is unreachable; retry later"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/clean/v1/type":{"get":{"summary":"Type Single","description":"Infer the type of one value without cleaning it. The inference block carries the\npredicted class, its probability and the alternatives considered.\n\nA fleet fault is reported in-band as na_code 3 with the message, not as a 5xx.","operationId":"type_single_clean_v1_type_get","parameters":[{"name":"value","in":"query","required":true,"schema":{"type":"string","description":"Value for which type must be inferred","title":"Value"},"description":"Value for which type must be inferred"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reserved; ignored","title":"Format"},"description":"Reserved; ignored"},{"name":"api_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API key; prefer the X-API-Key header","title":"Api Key"},"description":"API key; prefer the X-API-Key header"},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TypeResult"}}}},"401":{"description":"The request carries more values than the anonymous cap; a valid key in the X-API-Key header lifts it"},"413":{"description":"The request carries more values than the keyed cap; nothing lifts it"},"503":{"description":"The cleaning fleet is unreachable; retry later"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/clean/v1/types":{"post":{"summary":"Type Array","description":"Infer the common type of an array of values without cleaning them. One verdict for\nthe whole array, the way a column is typed.\n\nA fleet fault is reported in-band as na_code 3 with the message, not as a 5xx.","operationId":"type_array_clean_v1_types_post","parameters":[{"name":"api_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API key; prefer the X-API-Key header","title":"Api Key"},"description":"API key; prefer the X-API-Key header"},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TypeArrayRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TypeResult"}}}},"401":{"description":"The request carries more values than the anonymous cap; a valid key in the X-API-Key header lifts it"},"413":{"description":"The request carries more values than the keyed cap; nothing lifts it"},"503":{"description":"The cleaning fleet is unreachable; retry later"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/clean/v1/datetime":{"get":{"summary":"Datetime Single","description":"Clean one datetime string. Same answer shape as GET /value with entity=datetime,\nbut a valid API key is required.","operationId":"datetime_single_clean_v1_datetime_get","parameters":[{"name":"value","in":"query","required":true,"schema":{"type":"string","description":"Datetime string to clean","title":"Value"},"description":"Datetime string to clean"},{"name":"api_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API key; prefer the X-API-Key header","title":"Api Key"},"description":"API key; prefer the X-API-Key header"},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleanedValue"}}}},"401":{"description":"No valid API key; send one in the X-API-Key header"},"413":{"description":"The request carries more values than the keyed cap; nothing lifts it"},"503":{"description":"The cleaning fleet is unreachable; retry later"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Datetime Column","description":"Clean a column of datetime strings. Same answer shape as POST /column with\nentity=datetime, but a valid API key is required. The body is a bare JSON array,\nso the key travels in the X-API-Key header or the api_key query parameter.","operationId":"datetime_column_clean_v1_datetime_post","parameters":[{"name":"api_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API key; prefer the X-API-Key header","title":"Api Key"},"description":"API key; prefer the X-API-Key header"},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"description":"Datetime strings to clean, a bare JSON array","title":"Values"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleanResult"}}}},"401":{"description":"No valid API key; send one in the X-API-Key header"},"413":{"description":"The request carries more values than the keyed cap; nothing lifts it"},"503":{"description":"The cleaning fleet is unreachable; retry later"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"CleanRequest":{"properties":{"values":{"items":{"type":"string"},"type":"array","title":"Values","description":"The values to clean, one string per cell"},"entity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity","description":"Entity type hint, case-insensitive (e.g. \"date\", \"currency\", \"number\"); omit to auto-detect. GET /info lists the accepted values."},"column_name":{"type":"string","title":"Column Name","description":"A label for the column; appears in logs only","default":"column"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","description":"API key for clients that cannot set headers; the X-API-Key header is preferred"}},"type":"object","required":["values"],"title":"CleanRequest","description":"Request to clean a column of values (POST /column).\nSent from Excel via VBA, curl or Python."},"CleanResult":{"properties":{"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type","description":"The entity type the column was cleaned as: inferred, or the hint when it was honoured"},"entity_confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Entity Confidence","description":"Inference confidence in [0, 1]; null when a hint skipped inference"},"results":{"items":{"$ref":"#/components/schemas/CleanedCell"},"type":"array","title":"Results","description":"One cell per input value, same order"}},"type":"object","required":["results"],"title":"CleanResult","description":"Full cleaning result for a column."},"CleanedCell":{"properties":{"raw":{"type":"string","title":"Raw","description":"The input value, unchanged"},"clean":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Clean","description":"The cleaned value, canonical form"},"na_code":{"type":"integer","title":"Na Code","description":"Status code: 0 valid, 1 missing, 2 invalid, 3 error, 4 warning","default":0},"is_ambiguous":{"type":"boolean","title":"Is Ambiguous","description":"True when more than one reading is plausible and the caller should disambiguate","default":false},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence","description":"Parser confidence in [0, 1], when available"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable status; \"OK\" for a valid cell"}},"type":"object","required":["raw"],"title":"CleanedCell","description":"One cleaned cell result, Excel-friendly."},"CleanedValue":{"properties":{"clean":{"type":"string","title":"Clean","description":"The cleaned value; the input unchanged when nothing was cleaned"},"type":{"type":"string","title":"Type","description":"The entity type inferred, or the hint when honoured; \"\" if unknown"},"na_code":{"type":"integer","title":"Na Code","description":"Status code: 0 valid, 1 missing, 2 invalid, 3 error, 4 warning"},"is_ambiguous":{"type":"boolean","title":"Is Ambiguous","description":"True when more than one reading is plausible"},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence","description":"Parser confidence in [0, 1], when available"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable status"}},"type":"object","required":["clean","type","na_code","is_ambiguous"],"title":"CleanedValue","description":"One cleaned value (GET /value, GET /datetime). Always JSON: VBA needs the type to convert."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResult":{"properties":{"status":{"type":"string","title":"Status","description":"\"ok\" when this process is up"},"service":{"type":"string","title":"Service","description":"Service name"},"prefix":{"type":"string","title":"Prefix","description":"The URL prefix every endpoint lives under"}},"type":"object","required":["status","service","prefix"],"title":"HealthResult","description":"Liveness (GET /health): this process answers. Says nothing about the fleet behind it."},"InfoResult":{"properties":{"service":{"type":"string","title":"Service","description":"Service name"},"version":{"type":"string","title":"Version","description":"Service version"},"prefix":{"type":"string","title":"Prefix","description":"The URL prefix every endpoint lives under"},"anonymous_max_values":{"type":"integer","title":"Anonymous Max Values","description":"Values per request without a key; 0 means a key is mandatory"},"keyed_max_values":{"type":"integer","title":"Keyed Max Values","description":"Values per request with a valid key"},"entities":{"items":{"type":"string"},"type":"array","title":"Entities","description":"Accepted values of the entity hint, as the fleet registers them; matched case-insensitively"},"cleaning_entities":{"items":{"type":"string"},"type":"array","title":"Cleaning Entities","description":"The subset with a dedicated cleaner; a hint outside it types the values and returns them as text"},"key_required":{"items":{"type":"string"},"type":"array","title":"Key Required","description":"Paths under the prefix that refuse an anonymous caller (401)"},"auth":{"type":"object","title":"Auth","description":"How a key travels: header (preferred) and query parameter"},"cors":{"type":"object","title":"Cors","description":"Browser policy: any origin may call, GET and POST"},"openapi":{"type":"string","title":"Openapi","description":"Path of the OpenAPI document under the prefix"}},"type":"object","required":["service","version","prefix","anonymous_max_values","keyed_max_values","entities","cleaning_entities","key_required","auth","cors","openapi"],"title":"InfoResult","description":"What a caller needs before the first request (GET /info): caps, hints, the auth scheme."},"TypeArrayRequest":{"properties":{"data":{"items":{"type":"string"},"type":"array","title":"Data","description":"The values whose common type is to be inferred"},"job_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Uuid"},"session_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Uuid"},"column_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Column Id"},"column_name":{"type":"string","title":"Column Name","description":"A label for the array; appears in logs only","default":"range"},"page_num":{"type":"integer","title":"Page Num","default":0},"skip_cleaning":{"type":"boolean","title":"Skip Cleaning","default":false},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format","description":"Reserved; ignored"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","description":"API key for clients that cannot set headers; the X-API-Key header is preferred"}},"type":"object","required":["data"],"title":"TypeArrayRequest","description":"Request to infer the type of an array of values (POST /types)."},"TypeResult":{"properties":{"type":{"type":"string","title":"Type","description":"The inferred entity type; \"\" when none was inferred"},"inference":{"type":"object","title":"Inference","description":"The full inference block: predicted_class, class_p and the alternatives considered"}},"type":"object","required":["type","inference"],"title":"TypeResult","description":"Type inference result (GET /type, POST /types)."},"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"}}}}