{
  "openapi": "3.1.0",
  "info": {
    "title": "Atlas Contrôle — client API",
    "version": "1 (2026-10-06)",
    "description": "Read-only access to the data your organisation sees in the Atlas Contrôle client portal. Keys are requested by an organisation administrator in the client portal and approved by Atlas. Rate limits: 60 requests per minute and 10000 per day per key. Server-to-server only: requests carrying an Origin header are refused. Writes (#5725): each write is one gesture of the client space, judged by the client space with the rights of the key owner, and allowed only when Atlas granted the key the matching scope. Every write needs an Idempotency-Key header and counts in a daily write quota (600 per key), on top of the general limits. Payments: the API only returns the ERP payment link of an invoice (no card payment, no order payment). Not available through the API for now: sharing a report link, remediation photos, batch signing, the GDPR data export."
  },
  "servers": [
    {
      "url": "https://api.astro.atlascontrole.be/api/client/v1"
    }
  ],
  "security": [
    {
      "bearerKey": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key (`atk_…`) in the Authorization header. Never in a URL.",
        "x-scopes": {
          "read": "Read (inspections, orders, invoices, properties)",
          "documents:write": "Upload documents (diagrams and plans)",
          "messages:write": "Post messages on an order or inspection thread",
          "orders:write": "Place orders, request quotes, withdraw from an order",
          "quotes:sign": "Sign and confirm a quote",
          "appointments:write": "Ask to move or cancel a visit",
          "complaints:write": "File a complaint and reply to it",
          "payments": "Get an invoice’s online payment link",
          "sites:write": "Add or archive a property",
          "organization:write": "Manage the organisation’s members and billing address",
          "directory:write": "Manage your client and contractor records",
          "sharing:write": "Give or remove a contractor’s access to your orders"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code (see the documentation)."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation (English)."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Extra facts about the refusal (e.g. file, limitBytes, required scope)."
              }
            }
          }
        }
      },
      "Inspection": {
        "type": "object",
        "description": "One inspection (visit) of your organisation.",
        "required": [
          "ref",
          "state",
          "completion",
          "scheduledStart",
          "scheduledEnd",
          "type",
          "subtype",
          "title",
          "address",
          "inspectorName",
          "order",
          "report",
          "nextDueDate",
          "updatedAt"
        ],
        "properties": {
          "ref": {
            "type": "string",
            "description": "Inspection reference (report number), e.g. \"DOM-2026-ABC123\"."
          },
          "state": {
            "type": "string",
            "description": "Platform state: todo | in_progress | ready | done | cancelled | change_requested."
          },
          "completion": {
            "type": "object",
            "required": [
              "status",
              "doneIn"
            ],
            "properties": {
              "status": {
                "type": "string",
                "description": "done | cancelled | not-done"
              },
              "doneIn": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "astro | odoo | null"
              }
            }
          },
          "scheduledStart": {
            "type": [
              "string",
              "null"
            ],
            "description": "Visit window start (ISO 8601 instant)."
          },
          "scheduledEnd": {
            "type": [
              "string",
              "null"
            ],
            "description": "Visit window end (ISO 8601 instant)."
          },
          "type": {
            "type": "object",
            "description": "Inspection type label.",
            "required": [
              "fr",
              "nl"
            ],
            "properties": {
              "fr": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "nl": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "subtype": {
            "type": "object",
            "description": "Inspection subtype label.",
            "required": [
              "fr",
              "nl"
            ],
            "properties": {
              "fr": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "nl": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": "object",
            "description": "Inspected address (the complement is localised).",
            "required": [
              "fr",
              "nl"
            ],
            "properties": {
              "fr": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "nl": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "inspectorName": {
            "type": [
              "string",
              "null"
            ]
          },
          "order": {
            "type": "object",
            "required": [
              "id",
              "reference",
              "clientReference",
              "paymentStatus"
            ],
            "properties": {
              "id": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Order id (as used by /orders)."
              },
              "reference": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Order reference, e.g. \"S193525\"."
              },
              "clientReference": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Your own reference on the order."
              },
              "paymentStatus": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "invoiced | paid | null"
              }
            }
          },
          "report": {
            "type": "object",
            "required": [
              "status",
              "conclusion",
              "issuedConclusion",
              "held",
              "pdf",
              "portalUrl"
            ],
            "properties": {
              "status": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "platform | held | awaiting-payment | pending | unavailable | unverified | null"
              },
              "conclusion": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Current verdict (after remediations)."
              },
              "issuedConclusion": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Verdict printed on the issued report."
              },
              "held": {
                "type": "boolean",
                "description": "Corrections requested before the report is sent."
              },
              "pdf": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Signed links to the report PDF (no key needed: the link itself is the credential, like the QR on the report).",
                "required": [
                  "fr",
                  "nl"
                ],
                "properties": {
                  "fr": {
                    "type": "string"
                  },
                  "nl": {
                    "type": "string"
                  }
                }
              },
              "portalUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The report page in the client portal."
              }
            }
          },
          "nextDueDate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Next periodic inspection due date (YYYY-MM-DD)."
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last change of the inspection record (ISO 8601)."
          }
        }
      },
      "Order": {
        "type": "object",
        "required": [
          "id",
          "reference",
          "state",
          "dateOrder",
          "commitmentDate",
          "amountUntaxed",
          "amountTotal",
          "invoiceStatus",
          "paymentState",
          "clientReference",
          "contractor",
          "isQuotation"
        ],
        "properties": {
          "id": {
            "type": "number"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "sent (quotation) | sale | done | cancel"
          },
          "dateOrder": {
            "type": [
              "string",
              "null"
            ]
          },
          "commitmentDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "amountUntaxed": {
            "type": [
              "number",
              "null"
            ],
            "description": "null when prices are hidden for your account."
          },
          "amountTotal": {
            "type": [
              "number",
              "null"
            ],
            "description": "null when prices are hidden for your account."
          },
          "invoiceStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "paymentState": {
            "type": [
              "string",
              "null"
            ]
          },
          "clientReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "contractor": {
            "type": [
              "string",
              "null"
            ]
          },
          "isQuotation": {
            "type": "boolean"
          }
        }
      },
      "Invoice": {
        "type": "object",
        "required": [
          "id",
          "number",
          "date",
          "dueDate",
          "amountTotal",
          "amountResidual",
          "paymentState",
          "type",
          "orders"
        ],
        "properties": {
          "id": {
            "type": "number"
          },
          "number": {
            "type": [
              "string",
              "null"
            ]
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "description": "YYYY-MM-DD"
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "description": "YYYY-MM-DD"
          },
          "amountTotal": {
            "type": "number"
          },
          "amountResidual": {
            "type": "number"
          },
          "paymentState": {
            "type": "string",
            "description": "not_paid | in_payment | paid | partial | reversed"
          },
          "type": {
            "type": "string",
            "description": "invoice | credit_note"
          },
          "orders": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "reference"
              ],
              "properties": {
                "id": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "reference": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Site": {
        "type": "object",
        "required": [
          "key",
          "address",
          "installations"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Stable key of the site (address unit)."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "installations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "reference",
                "type",
                "lastInspectionRef",
                "lastInspectionAt",
                "nextDueDate",
                "conclusion"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "reference": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "type": {
                  "type": "object",
                  "description": "Installation type label.",
                  "required": [
                    "fr",
                    "nl"
                  ],
                  "properties": {
                    "fr": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "nl": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "lastInspectionRef": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "lastInspectionAt": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "nextDueDate": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "conclusion": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "createdAt",
          "from",
          "author",
          "body",
          "retracted",
          "attachments",
          "complaintReference",
          "readByAtlas"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "Instant (UTC)."
          },
          "from": {
            "type": "string",
            "description": "you (your organisation, from the portal or this API) | atlas (Atlas Contrôle staff) | assistant (Nova, the automatic assistant)"
          },
          "author": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name of the author."
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "Text of the message; null when retracted."
          },
          "retracted": {
            "type": "boolean"
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "url"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "description": "Download URL — send your API key in the Authorization header."
                }
              }
            }
          },
          "complaintReference": {
            "type": [
              "string",
              "null"
            ],
            "description": "PL-… when the message opened a complaint file."
          },
          "readByAtlas": {
            "type": "boolean",
            "description": "Your messages only: true once Atlas staff opened them."
          }
        }
      },
      "Me": {
        "type": "object",
        "required": [
          "apiVersion",
          "key",
          "organisation",
          "scopes",
          "pendingScopes",
          "limits",
          "usage"
        ],
        "properties": {
          "apiVersion": {
            "type": "string"
          },
          "key": {
            "type": "object",
            "required": [
              "id",
              "name",
              "prefix",
              "expiresAt"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "expiresAt": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "organisation": {
            "type": "object",
            "required": [
              "partnerId",
              "name"
            ],
            "properties": {
              "partnerId": {
                "type": "number"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "pendingScopes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "limits": {
            "type": "object",
            "required": [
              "perMinute",
              "perDay",
              "writesPerDay"
            ],
            "properties": {
              "perMinute": {
                "type": "number"
              },
              "perDay": {
                "type": "number"
              },
              "writesPerDay": {
                "type": "number"
              }
            }
          },
          "usage": {
            "type": "object",
            "required": [
              "today",
              "writesToday"
            ],
            "properties": {
              "today": {
                "type": "number"
              },
              "writesToday": {
                "type": "number"
              }
            }
          }
        }
      },
      "WriteResult": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "What the client space answered for this gesture (same fields)."
          }
        }
      }
    },
    "headers": {
      "ETag": {
        "description": "Send it back in If-None-Match: 304 when unchanged.",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests allowed per minute.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current minute.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Seconds until the current minute window resets.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "invalid_parameter",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "unauthorized | key_revoked | key_expired | key_not_active",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "forbidden | browser_origin_refused | invoices_forbidden",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "not_found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "rate_limited | quota_exceeded — retry after `Retry-After` seconds.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait.",
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "Unavailable": {
        "description": "disabled | upstream_unavailable",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A unique key per gesture (8–255 visible ASCII characters, e.g. a UUID). Retrying with the same key replays the first answer (header Idempotent-Replayed: true) instead of doing the gesture twice. Kept 24 hours.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 255
        }
      }
    }
  },
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "summary": "The key, its organisation, limits and today’s usage.",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/inspections": {
      "get": {
        "operationId": "listInspections",
        "summary": "Inspections of your organisation, oldest change first (sync-friendly).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Platform state filter.",
            "schema": {
              "type": "string",
              "enum": [
                "todo",
                "in_progress",
                "ready",
                "done",
                "cancelled",
                "change_requested"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Visit date ≥ (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Visit date ≤ (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "required": false,
            "description": "Only records changed at or after this instant (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1–100 (default 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page (`nextCursor`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Inspection"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to get the next page; null at the end."
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Not modified"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/inspections/{ref}": {
      "get": {
        "operationId": "getInspection",
        "summary": "One inspection by its reference.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Inspection"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/orders": {
      "get": {
        "operationId": "listOrders",
        "summary": "Quotations and orders, the 200 most recent by order date (refreshed at most once a minute per key).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "quotation | order | cancelled",
            "schema": {
              "type": "string",
              "enum": [
                "quotation",
                "order",
                "cancelled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1–100 (default 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page (`nextCursor`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor",
                    "truncated"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to get the next page; null at the end."
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "true when the list reached its cap of 200 orders (the most recent by order date, counted before the status filter): older orders may exist and are not returned, even when nextCursor is null."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "orderCreate",
        "summary": "Place an order, exactly as the client portal does (same prices, slots, confirmation email).",
        "description": "The order is confirmed in your name; the confirmation email goes to the key owner. The Idempotency-Key also becomes the order request id: the same key never creates a second order (data.reused = true on a replay). A slotToken comes from POST /orders/slots; without one, pass callback or planner: true as in the client portal. Scope: `orders:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, slot_expired, slot_taken, cart_changed, invalid_slot, phone_required, site_contact_invalid, site_contact_phone_invalid, site_is_orderer_address, documents_invalid, order_failed, price_unavailable.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lines": {
                    "type": "array",
                    "maxItems": 10,
                    "description": "Basket lines: [{ service, options, qty }] — service keys and options from GET /catalog.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "service": {
                          "type": "string"
                        },
                        "options": {
                          "type": "object"
                        },
                        "qty": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "service"
                      ]
                    }
                  },
                  "service": {
                    "type": "string"
                  },
                  "address": {
                    "type": "object",
                    "properties": {
                      "street": {
                        "type": "string"
                      },
                      "zip": {
                        "type": "string"
                      },
                      "city": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "street",
                      "zip",
                      "city"
                    ]
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone of the person PLACING the order (your contact): the office calls this number about the order. It is not the number of the occupant on site — send that in endClient.phone, and in siteContact to make that person the appointment contact."
                  },
                  "slotToken": {
                    "type": "string"
                  },
                  "callback": {
                    "type": "object"
                  },
                  "planner": {
                    "type": "boolean"
                  },
                  "clientReference": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "endClient": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "End client (occupant / owner) — names the site in our records."
                      },
                      "phone": {
                        "type": "string",
                        "description": "Phone of the end client on site: shown to the inspector on the inspection. Send it here, not only in the top-level phone."
                      },
                      "email": {
                        "type": "string"
                      }
                    },
                    "description": "The client on site, when it is not you (agency, installer, property manager ordering for a customer)."
                  },
                  "siteContact": {
                    "type": "object",
                    "properties": {
                      "role": {
                        "type": "string",
                        "enum": [
                          "end_client",
                          "third_party",
                          "orderer"
                        ],
                        "description": "Who opens the door: the end client, an agency / third party, or your own team."
                      },
                      "name": {
                        "type": "string",
                        "maxLength": 80
                      },
                      "phone": {
                        "type": "string",
                        "description": "Belgian phone number; required."
                      }
                    },
                    "required": [
                      "role",
                      "phone"
                    ],
                    "description": "Optional — the APPOINTMENT contact: the office proposes the date to this person and the day-before reminder goes to them; you keep the confirmation email. Without it, the appointment contact is you (top-level phone)."
                  },
                  "siteName": {
                    "type": "string"
                  },
                  "contractor": {
                    "type": "object"
                  },
                  "plannableFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "plannableBefore": {
                    "type": "string",
                    "format": "date"
                  },
                  "documents": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  }
                },
                "required": [
                  "address",
                  "phone"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/invoices": {
      "get": {
        "operationId": "listInvoices",
        "summary": "Posted invoices and credit notes (organisation administrators only; 20 per page).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "paid | unpaid",
            "schema": {
              "type": "string",
              "enum": [
                "paid",
                "unpaid"
              ]
            }
          },
          {
            "name": "year",
            "in": "query",
            "required": false,
            "description": "Invoice year (YYYY).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page (`nextCursor`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to get the next page; null at the end."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/orders/{odooId}/messages": {
      "get": {
        "operationId": "listOrderMessages",
        "summary": "Messages of the order thread, newest first (50 per page), as the client portal shows them.",
        "description": "Atlas replies, your own messages (portal or API) and the automatic assistant. Internal Atlas notes are never returned. Reading through the API does not mark messages as read in the client portal.",
        "parameters": [
          {
            "name": "odooId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page (`nextCursor`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to get the next page; null at the end."
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Not modified"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "orderMessage",
        "summary": "Post a message on an order thread (photos / PDF allowed).",
        "description": "Scope: `messages:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, empty_body, body_too_long, attachment_type, not_found, not_linked.",
        "x-scope": "messages:write",
        "security": [
          {
            "bearerKey": [
              "messages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "odooId",
            "in": "path",
            "description": "Order id (Order.id).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 3000
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/{odooId}/messages/attachments/{attachmentId}": {
      "get": {
        "operationId": "listOrderMessagesAttachment",
        "summary": "Download one attachment of a message of this thread (Message.attachments[].url).",
        "parameters": [
          {
            "name": "odooId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "attachmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file (PDF or image)",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/inspections/{ref}/messages": {
      "get": {
        "operationId": "listInspectionMessages",
        "summary": "Messages of the inspection thread, newest first (50 per page), as the client portal shows them.",
        "description": "Atlas replies, your own messages (portal or API) and the automatic assistant. Internal Atlas notes are never returned. Reading through the API does not mark messages as read in the client portal.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page (`nextCursor`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to get the next page; null at the end."
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Not modified"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "inspectionMessage",
        "summary": "Post a message on an inspection thread.",
        "description": "Scope: `messages:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, empty_body, body_too_long, attachment_type, not_found, not_linked.",
        "x-scope": "messages:write",
        "security": [
          {
            "bearerKey": [
              "messages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "description": "Inspection reference (Inspection.ref).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 3000
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inspections/{ref}/messages/attachments/{attachmentId}": {
      "get": {
        "operationId": "listInspectionMessagesAttachment",
        "summary": "Download one attachment of a message of this thread (Message.attachments[].url).",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "attachmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file (PDF or image)",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sites": {
      "get": {
        "operationId": "listSites",
        "summary": "Your sites (addresses) and their installations (20 per page).",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page (`nextCursor`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Site"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to get the next page; null at the end."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "siteCreate",
        "summary": "Add a site (address checked against the bpost register).",
        "description": "Scope: `sites:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, address_not_found.",
        "x-scope": "sites:write",
        "security": [
          {
            "bearerKey": [
              "sites:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "street": {
                    "type": "string"
                  },
                  "zip": {
                    "type": "string",
                    "pattern": "^\\d{4}$"
                  },
                  "city": {
                    "type": "string"
                  },
                  "complement": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  }
                },
                "required": [
                  "street",
                  "zip",
                  "city"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/catalog": {
      "get": {
        "operationId": "getCatalog",
        "summary": "What you can order, as the client space shows it to the key owner: services, options, your prices, document limits.",
        "description": "Compose the `lines` of POST /orders and POST /orders/pricing from it: [{ \"service\": <key>, \"options\": {…}, \"qty\": 1 }]. Refreshed at most once a minute per key. Scope: `read`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/orders/{odooId}/documents": {
      "post": {
        "operationId": "orderDocument",
        "summary": "Upload diagrams and plans to an order (client portal “Add a document”).",
        "description": "Same slot as the client portal: the files appear under “Diagrams and plans” of the order, linked to the visits you name (taskRefs) or to all of them. File types are checked against the bytes; limits are those of the client portal. Scope: `documents:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, invalid_doc_type, invalid_visits, foreign_visit, attachment_type, attachment_too_large, attachments_too_large, attachments_too_many, not_found.",
        "x-scope": "documents:write",
        "security": [
          {
            "bearerKey": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "odooId",
            "in": "path",
            "description": "Order id (Order.id).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "annexDocType": {
                    "type": "string",
                    "enum": [
                      "unifilaire",
                      "position",
                      "other"
                    ],
                    "description": "Document type, as in the client portal “Add a document”: unifilaire (single-line diagram), position (location plan) or other."
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  },
                  "taskRefs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Inspection references of this order the documents concern (default: all)."
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 3000,
                    "description": "Optional note."
                  }
                },
                "required": [
                  "annexDocType",
                  "attachments"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inspections/{ref}/documents": {
      "post": {
        "operationId": "inspectionDocument",
        "summary": "Upload diagrams and plans to one inspection.",
        "description": "Scope: `documents:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, invalid_doc_type, attachment_type, attachment_too_large, not_found.",
        "x-scope": "documents:write",
        "security": [
          {
            "bearerKey": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "description": "Inspection reference (Inspection.ref).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "annexDocType": {
                    "type": "string",
                    "enum": [
                      "unifilaire",
                      "position",
                      "other"
                    ],
                    "description": "Document type, as in the client portal “Add a document”: unifilaire (single-line diagram), position (location plan) or other."
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 3000
                  }
                },
                "required": [
                  "annexDocType",
                  "attachments"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/{odooId}/complaints": {
      "post": {
        "operationId": "orderComplaint",
        "summary": "File a complaint about an order (opens a PL-… complaint file, acknowledged by email).",
        "description": "Scope: `complaints:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, empty_body, complaint_failed, not_found.",
        "x-scope": "complaints:write",
        "security": [
          {
            "bearerKey": [
              "complaints:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "odooId",
            "in": "path",
            "description": "Order id (Order.id).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 3000
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inspections/{ref}/complaints": {
      "post": {
        "operationId": "inspectionComplaint",
        "summary": "File a complaint about an inspection.",
        "description": "Scope: `complaints:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, empty_body, complaint_failed, not_found.",
        "x-scope": "complaints:write",
        "security": [
          {
            "bearerKey": [
              "complaints:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "description": "Inspection reference (Inspection.ref).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 3000
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/complaints/{reference}/messages": {
      "post": {
        "operationId": "complaintMessage",
        "summary": "Reply inside one of your complaint files.",
        "description": "Scope: `complaints:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, empty_body, body_too_long, not_found.",
        "x-scope": "complaints:write",
        "security": [
          {
            "bearerKey": [
              "complaints:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "description": "Complaint reference (PL-…).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 6000
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "mimeType",
                        "dataBase64"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 150
                        },
                        "mimeType": {
                          "type": "string",
                          "enum": [
                            "application/pdf",
                            "image/jpeg",
                            "image/png",
                            "image/webp",
                            "image/heic",
                            "image/heif"
                          ],
                          "description": "Checked against the file bytes: a mismatch is refused (attachment_type)."
                        },
                        "dataBase64": {
                          "type": "string",
                          "description": "The file, base64-encoded."
                        }
                      }
                    },
                    "description": "Up to 5 files per request: PDF ≤ 15 MiB, image ≤ 5 MiB, 30 MiB in total."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/pricing": {
      "post": {
        "operationId": "orderPricing",
        "summary": "Price a basket exactly as the client portal does (writes nothing).",
        "description": "Same body as POST /orders (lines or service). No Idempotency-Key needed. Scope: `orders:write`. Writes nothing: no Idempotency-Key, no write quota. Refusals: reinspection_not_eligible, price_unavailable.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lines": {
                    "type": "array"
                  },
                  "service": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/slots": {
      "post": {
        "operationId": "orderSlots",
        "summary": "Propose bookable visit slots for a basket and address (writes nothing).",
        "description": "Returns signed slot tokens; pass one as slotToken to POST /orders before it expires. No Idempotency-Key needed. Scope: `orders:write`. Writes nothing: no Idempotency-Key, no write quota.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lines": {
                    "type": "array"
                  },
                  "service": {
                    "type": "string"
                  },
                  "address": {
                    "type": "object",
                    "properties": {
                      "street": {
                        "type": "string"
                      },
                      "zip": {
                        "type": "string"
                      },
                      "city": {
                        "type": "string"
                      }
                    }
                  },
                  "plannableFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "plannableBefore": {
                    "type": "string",
                    "format": "date"
                  }
                },
                "required": [
                  "address"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/quotes": {
      "post": {
        "operationId": "quoteRequest",
        "summary": "Request a quote (the office prices it; nothing is ordered).",
        "description": "Scope: `orders:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lines": {
                    "type": "array"
                  },
                  "cart": {
                    "type": "object"
                  },
                  "address": {
                    "type": "object"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "requestedSlot": {
                    "type": "object",
                    "properties": {
                      "date": {
                        "type": "string",
                        "format": "date"
                      },
                      "startTime": {
                        "type": "string"
                      },
                      "endTime": {
                        "type": "string"
                      }
                    }
                  }
                },
                "required": [
                  "address"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/quotes/{reference}/cancel": {
      "post": {
        "operationId": "quoteCancel",
        "summary": "Cancel a quote request you made.",
        "description": "Scope: `orders:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, not_cancellable, not_found.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "description": "Quote request reference.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/{id}/sign": {
      "post": {
        "operationId": "orderSign",
        "summary": "Sign and confirm a quote, as “Sign” in the client portal.",
        "description": "The signature is recorded with its evidence (the key owner, the key, the request), like a signature drawn in the client portal. Scope: `quotes:sign`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, not_signable, odoo_unreachable.",
        "x-scope": "quotes:sign",
        "security": [
          {
            "bearerKey": [
              "quotes:sign"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Quote id (Order.id).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "signedBy": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "signature": {
                    "type": "string",
                    "description": "Signature image (PNG, base64 or data URL, ≤ 200 000 characters)."
                  },
                  "termsAccepted": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "signedBy",
                  "signature"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/{id}/withdrawal": {
      "post": {
        "operationId": "orderWithdrawal",
        "summary": "Declare a withdrawal from an order (consumer right, when the client portal offers it).",
        "description": "Scope: `orders:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, withdrawal_closed, not_found.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Order id (Order.id).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/requests": {
      "post": {
        "operationId": "serviceRequest",
        "summary": "Ask Atlas to contact you about a service (no price, no order).",
        "description": "Scope: `orders:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "orders:write",
        "security": [
          {
            "bearerKey": [
              "orders:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "service": {
                    "type": "string",
                    "enum": [
                      "elec",
                      "gaz",
                      "peb",
                      "incendie",
                      "borne",
                      "autre"
                    ]
                  },
                  "address": {
                    "type": "string",
                    "maxLength": 250
                  },
                  "notes": {
                    "type": "string"
                  },
                  "preferredDate": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "reportReference": {
                    "type": "string"
                  }
                },
                "required": [
                  "service"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inspections/{ref}/visit-change": {
      "post": {
        "operationId": "visitChange",
        "summary": "Ask to move (reschedule) or cancel a planned visit.",
        "description": "Same request as the client portal: the office and the inspector are notified; one open request per visit. Scope: `appointments:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, visit_not_open, request_open, not_found.",
        "x-scope": "appointments:write",
        "security": [
          {
            "bearerKey": [
              "appointments:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "description": "Inspection reference (Inspection.ref).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "reschedule",
                      "cancel"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "minLength": 3
                  },
                  "preferredDates": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "date": {
                          "type": "string",
                          "format": "date"
                        },
                        "daypart": {
                          "type": "string",
                          "enum": [
                            "am",
                            "pm",
                            "day"
                          ]
                        }
                      }
                    }
                  },
                  "phone": {
                    "type": "string"
                  },
                  "lang": {
                    "type": "string",
                    "enum": [
                      "fr",
                      "nl",
                      "en"
                    ]
                  }
                },
                "required": [
                  "action",
                  "reason"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{odooId}/payment-link": {
      "post": {
        "operationId": "invoicePaymentLink",
        "summary": "Get the online payment link of an invoice (organisation administrators).",
        "description": "The only payment action of the API: the ERP payment link of a posted invoice, to hand to whoever pays. No card payment (Stripe PaymentIntent) and no payment of an order through the API. Scope: `payments`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, already_paid, invoices_admin_only, payment_unavailable, not_found.",
        "x-scope": "payments",
        "security": [
          {
            "bearerKey": [
              "payments"
            ]
          }
        ],
        "parameters": [
          {
            "name": "odooId",
            "in": "path",
            "description": "Invoice id (Invoice.id).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sites/archive": {
      "post": {
        "operationId": "siteArchive",
        "summary": "Archive a site without history.",
        "description": "Scope: `sites:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, has_history.",
        "x-scope": "sites:write",
        "security": [
          {
            "bearerKey": [
              "sites:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Site.key"
                  }
                },
                "required": [
                  "key"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/organization/invitations": {
      "post": {
        "operationId": "orgInvite",
        "summary": "Invite a person into the organisation (they receive the client portal invitation email).",
        "description": "Scope: `organization:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, invalid_email, role_required, invalid_role, self, not_admin.",
        "x-scope": "organization:write",
        "security": [
          {
            "bearerKey": [
              "organization:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "admin",
                      "member"
                    ]
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "role"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/organization/invitations/revoke": {
      "post": {
        "operationId": "orgInviteRevoke",
        "summary": "Revoke a pending invitation.",
        "description": "Scope: `organization:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, not_pending, not_found.",
        "x-scope": "organization:write",
        "security": [
          {
            "bearerKey": [
              "organization:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/organization/members/role": {
      "post": {
        "operationId": "orgMemberRole",
        "summary": "Change a member’s role (admin / member).",
        "description": "Scope: `organization:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, not_a_member, last_admin, account_blocked.",
        "x-scope": "organization:write",
        "security": [
          {
            "bearerKey": [
              "organization:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "admin",
                      "member"
                    ]
                  }
                },
                "required": [
                  "email",
                  "role"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/organization/members/remove": {
      "post": {
        "operationId": "orgMemberRemove",
        "summary": "Remove a member from the organisation (their sessions — and keys — stop at once).",
        "description": "Scope: `organization:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, last_admin, not_found.",
        "x-scope": "organization:write",
        "security": [
          {
            "bearerKey": [
              "organization:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/organization/join-requests/decide": {
      "post": {
        "operationId": "orgJoinDecide",
        "summary": "Accept or refuse a request to join the organisation.",
        "description": "Scope: `organization:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, already_decided, expired, not_found.",
        "x-scope": "organization:write",
        "security": [
          {
            "bearerKey": [
              "organization:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "approve": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "id",
                  "approve"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/organization/billing": {
      "post": {
        "operationId": "orgBilling",
        "summary": "Set the email address invoices are sent to.",
        "description": "Scope: `organization:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, invalid_email.",
        "x-scope": "organization:write",
        "security": [
          {
            "bearerKey": [
              "organization:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clients": {
      "post": {
        "operationId": "clientCreate",
        "summary": "Add one of your own clients (syndic, landlord…) to your directory.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 160
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clients/update": {
      "post": {
        "operationId": "clientUpdate",
        "summary": "Update a client of your directory.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clients/archive": {
      "post": {
        "operationId": "clientArchive",
        "summary": "Archive (or restore, archived: false) a client of your directory.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "archived": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractors": {
      "post": {
        "operationId": "contractorCreate",
        "summary": "Add a contractor to your directory.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, exists, exists_archived, name_required, email_invalid.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractors/update": {
      "post": {
        "operationId": "contractorUpdate",
        "summary": "Update a contractor of your directory.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractors/archive": {
      "post": {
        "operationId": "contractorArchive",
        "summary": "Archive (or restore) a contractor.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "archived": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractors/merge": {
      "post": {
        "operationId": "contractorMerge",
        "summary": "Merge duplicate contractors into one.",
        "description": "Scope: `directory:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "directory:write",
        "security": [
          {
            "bearerKey": [
              "directory:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractor-links": {
      "post": {
        "operationId": "contractorLinkCreate",
        "summary": "Open a contractor’s access to some of your orders (returns the link to hand over).",
        "description": "Scope: `sharing:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact, invalid_orders, order_not_found, too_many_links, admin_only.",
        "x-scope": "sharing:write",
        "security": [
          {
            "bearerKey": [
              "sharing:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "orders": {
                    "type": "array",
                    "items": {
                      "type": [
                        "string",
                        "integer"
                      ]
                    }
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  }
                },
                "required": [
                  "orders"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractor-links/{id}/revoke": {
      "post": {
        "operationId": "contractorLinkRevoke",
        "summary": "Close a contractor link.",
        "description": "Scope: `sharing:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "sharing:write",
        "security": [
          {
            "bearerKey": [
              "sharing:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contractor-links/{id}/grants/{grantId}/revoke": {
      "post": {
        "operationId": "contractorGrantRevoke",
        "summary": "Cut one account’s access through a contractor link.",
        "description": "Scope: `sharing:write`. Refusals: insufficient_scope (403), idempotency_key_required / invalid_idempotency_key (400), idempotency_in_progress (409), idempotency_key_reused (422), write_quota_exceeded (429), not_linked (409) — the key owner is not linked to an organisation contact.",
        "x-scope": "sharing:write",
        "security": [
          {
            "bearerKey": [
              "sharing:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Done",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WriteResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict with the current state (see error.code)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "upstream_unavailable — retry later with the SAME Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}