{"openapi":"3.1.0","info":{"title":"Konu Middleware","version":"0.16.0"},"paths":{"/v1/queue/{odoo_model}":{"post":{"tags":["queue"],"summary":"Enqueue Create","description":"Queue the creation of an Odoo record. Returns 202 + queue_id immediately.","operationId":"enqueue_create_v1_queue__odoo_model__post","parameters":[{"name":"odoo_model","in":"path","required":true,"schema":{"type":"string","title":"Odoo Model"}},{"name":"n8n_workflow","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional N8N transform workflow to preprocess the payload before writing to Odoo (must be in the allow-list).","title":"N8N Workflow"},"description":"Optional N8N transform workflow to preprocess the payload before writing to Odoo (must be in the allow-list)."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnqueueRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["queue"],"summary":"Enqueue Bulk Update","description":"Queue a bulk update: write the SAME `payload` to every id in `ids`, in one\nOdoo write call. Returns 202 + queue_id.","operationId":"enqueue_bulk_update_v1_queue__odoo_model__patch","parameters":[{"name":"odoo_model","in":"path","required":true,"schema":{"type":"string","title":"Odoo Model"}},{"name":"n8n_workflow","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional N8N transform workflow to preprocess the payload (must be in the allow-list).","title":"N8N Workflow"},"description":"Optional N8N transform workflow to preprocess the payload (must be in the allow-list)."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpdateRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/queue/{odoo_model}/{odoo_id}":{"patch":{"tags":["queue"],"summary":"Enqueue Update","description":"Queue an update (Odoo write) of an existing record by its Odoo id.\nOnly the fields in `payload` are changed. Returns 202 + queue_id.","operationId":"enqueue_update_v1_queue__odoo_model___odoo_id__patch","parameters":[{"name":"odoo_model","in":"path","required":true,"schema":{"type":"string","title":"Odoo Model"}},{"name":"odoo_id","in":"path","required":true,"schema":{"type":"integer","title":"Odoo Id"}},{"name":"n8n_workflow","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional N8N transform workflow to preprocess the payload (must be in the allow-list).","title":"N8N Workflow"},"description":"Optional N8N transform workflow to preprocess the payload (must be in the allow-list)."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnqueueRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs":{"get":{"tags":["queue"],"summary":"List Jobs","description":"Recent jobs for the calling external, newest first. e.g. /v1/jobs?status=dead","operationId":"list_jobs_v1_jobs_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(needs_transform|transforming|pending|processing|done|failed|dead)$"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{queue_id}":{"get":{"tags":["queue"],"summary":"Job Status","description":"Polling fallback for externals that can't subscribe to MQTT (step 5).","operationId":"job_status_v1_jobs__queue_id__get","parameters":[{"name":"queue_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Queue Id"}},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/read/{odoo_model}":{"get":{"tags":["read"],"summary":"Read Model","operationId":"read_model_v1_read__odoo_model__get","parameters":[{"name":"odoo_model","in":"path","required":true,"schema":{"type":"string","title":"Odoo Model"}},{"name":"domain","in":"query","required":false,"schema":{"type":"string","description":"Odoo domain as a JSON array string","default":"[]","title":"Domain"},"description":"Odoo domain as a JSON array string"},{"name":"fields","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated field names ('' = all)","default":"","title":"Fields"},"description":"Comma-separated field names ('' = all)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":80,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"order","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"e.g. 'create_date desc'","title":"Order"},"description":"e.g. 'create_date desc'"},{"name":"n8n_read","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional read-transform workflow (must be in the allow-list). Applies request and/or response shaping via N8N.","title":"N8N Read"},"description":"Optional read-transform workflow (must be in the allow-list). Applies request and/or response shaping via N8N."},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/read-health":{"get":{"tags":["read"],"summary":"Read Health","description":"Surface breaker state for monitoring (step 9 scrapes this).","operationId":"read_health_v1_read_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/outbound":{"post":{"tags":["outbound"],"summary":"Receive Outbound","description":"Process a batch of outbound items for one workflow through N8N.\n\nThe middleware owns the per-item loop and the stop-on-first-failure\nguarantee (so every N8N workflow can stay simple: one item in, success or\nfailure out). Items are processed in order:\n\n  - success            -> {\"status\": \"sent\", \"queue_id\": ...}\n  - a transient error  -> {\"status\": \"failed\", \"error\": ...}      (Odoo retries)\n  - a permanent failure-> {\"status\": \"failed\", \"permanent\": true} (Odoo dead-letters)\n\nOn the FIRST failed item, processing stops: that item is reported failed and\nevery later item is reported \"not_attempted\" (Odoo keeps them pending, in\norder, behind the failure). This preserves per-workflow sequence.","operationId":"receive_outbound_v1_outbound_post","parameters":[{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutboundBatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/outbound/workflows":{"get":{"tags":["outbound"],"summary":"List Outbound Workflows","description":"Return the outbound workflows Odoo is allowed to ship to.\n\nSource of truth is the N8N_OUTBOUND_WORKFLOWS allow-list. Odoo pulls this\n(daily cron + on-demand) and stores the names so flows select from them\ninstead of free-typing — which is what prevents a typo shipping items to a\nworkflow the middleware would reject.","operationId":"list_outbound_workflows_v1_outbound_workflows_get","parameters":[{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/call/{odoo_model}/{method}":{"post":{"summary":"Call Method","description":"Synchronously invoke {method} on {odoo_model} in Odoo and return its result.\n\nRequires the per-method scope `{model}:call:{method}`. Blocks on Odoo (no\nqueuing). Odoo errors surface as 400; a transient/unavailable Odoo yields 503\nso the caller can retry.","operationId":"call_method_v1_call__odoo_model___method__post","parameters":[{"name":"odoo_model","in":"path","required":true,"schema":{"type":"string","title":"Odoo Model"}},{"name":"method","in":"path","required":true,"schema":{"type":"string","title":"Method"}},{"name":"n8n_workflow","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional N8N workflow to shape args/kwargs before the call (must be in the transform allow-list).","title":"N8N Workflow"},"description":"Optional N8N workflow to shape args/kwargs before the call (must be in the transform allow-list)."},{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MethodCallRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/ping":{"get":{"summary":"Ping","operationId":"ping_v1_ping_get","parameters":[{"name":"X-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BulkUpdateRequest":{"properties":{"payload":{"additionalProperties":true,"type":"object","title":"Payload","description":"Field values for the Odoo record"},"ids":{"items":{"type":"integer"},"type":"array","minItems":1,"title":"Ids","description":"Odoo ids to update. The same payload is written to all of them."}},"type":"object","required":["payload","ids"],"title":"BulkUpdateRequest"},"EnqueueRequest":{"properties":{"payload":{"additionalProperties":true,"type":"object","title":"Payload","description":"Field values for the Odoo record"}},"type":"object","required":["payload"],"title":"EnqueueRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MethodCallRequest":{"properties":{"args":{"items":{},"type":"array","title":"Args","description":"Positional arguments for the method (jsonrpc only), e.g. [[5,6]] for record ids. Not supported on ODOO_PROTOCOL=json2."},"kwargs":{"additionalProperties":true,"type":"object","title":"Kwargs","description":"Keyword arguments for the method, e.g. {\"context\": {...}}."}},"type":"object","title":"MethodCallRequest"},"OutboundBatch":{"properties":{"workflow":{"type":"string","title":"Workflow","description":"Target N8N workflow name."},"items":{"items":{"$ref":"#/components/schemas/OutboundItem"},"type":"array","minItems":1,"title":"Items"}},"type":"object","required":["workflow","items"],"title":"OutboundBatch"},"OutboundItem":{"properties":{"item_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Item Id"},"operation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Operation"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode"},"awaits_response":{"type":"boolean","title":"Awaits Response","default":false},"external":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External"},"res_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Res Id"},"identifiers":{"additionalProperties":true,"type":"object","title":"Identifiers"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"}},"type":"object","title":"OutboundItem"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}