{
  "components": {
    "headers": {
      "RateLimitLimit": {
        "description": "The per-key bucket size in effect for this endpoint.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitReset": {
        "description": "Unix epoch seconds when the window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "RequestId": {
        "description": "Echoes the request id minted for this call. Present on every response, including errors.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying. Present on 429 and on the two idempotency 409s.",
        "schema": {
          "type": "integer"
        }
      },
      "UsageWarning": {
        "description": "Present only on a 2xx: \"over-limit\" (daily ceiling reached, request still served while admission is in `observe` mode) or \"credits-exhausted\" (prepaid balance non-positive).",
        "schema": {
          "enum": [
            "over-limit",
            "credits-exhausted",
            "admission-unavailable"
          ],
          "type": "string"
        }
      }
    },
    "schemas": {
      "AvailabilityDay": {
        "additionalProperties": false,
        "properties": {
          "date": {
            "description": "YYYY-MM-DD, business-local calendar date.",
            "format": "date",
            "type": "string"
          },
          "slots": {
            "items": {
              "description": "HH:mm business-local wall clock.",
              "pattern": "^\\d{2}:\\d{2}$",
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "date",
          "slots"
        ],
        "type": "object"
      },
      "Booking": {
        "additionalProperties": false,
        "properties": {
          "customer": {
            "additionalProperties": false,
            "properties": {
              "id": {
                "description": "Null when no customer-book row resolves for this booking's phone.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "nameMasked": {
                "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                "type": "string"
              },
              "phoneMasked": {
                "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                "type": "string"
              }
            },
            "required": [
              "id",
              "nameMasked",
              "phoneMasked"
            ],
            "type": "object"
          },
          "durationMinutes": {
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "serviceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "serviceNameAr": {
            "maxLength": 200,
            "type": "string"
          },
          "serviceNameEn": {
            "maxLength": 200,
            "type": "string"
          },
          "startsAt": {
            "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "timezone": {
            "description": "IANA zone id, e.g. Asia/Qatar.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "serviceId",
          "serviceNameEn",
          "serviceNameAr",
          "startsAt",
          "durationMinutes",
          "timezone",
          "customer"
        ],
        "type": "object"
      },
      "BookingCancelRequest": {
        "additionalProperties": false,
        "properties": {
          "action": {
            "const": "cancel",
            "type": "string"
          },
          "reason": {
            "maxLength": 500,
            "type": "string"
          }
        },
        "required": [
          "action"
        ],
        "type": "object"
      },
      "BookingChangeItem": {
        "additionalProperties": false,
        "properties": {
          "changedAt": {
            "format": "date-time",
            "type": "string"
          },
          "customer": {
            "additionalProperties": false,
            "properties": {
              "id": {
                "description": "Null when no customer-book row resolves for this booking's phone.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "nameMasked": {
                "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                "type": "string"
              },
              "phoneMasked": {
                "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                "type": "string"
              }
            },
            "required": [
              "id",
              "nameMasked",
              "phoneMasked"
            ],
            "type": "object"
          },
          "durationMinutes": {
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "serviceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "serviceNameAr": {
            "maxLength": 200,
            "type": "string"
          },
          "serviceNameEn": {
            "maxLength": 200,
            "type": "string"
          },
          "startsAt": {
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "serviceId",
          "serviceNameEn",
          "serviceNameAr",
          "startsAt",
          "durationMinutes",
          "timezone",
          "customer",
          "changedAt"
        ],
        "type": "object"
      },
      "BookingCustomer": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "description": "Null when no customer-book row resolves for this booking's phone.",
            "type": [
              "string",
              "null"
            ]
          },
          "nameMasked": {
            "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
            "type": "string"
          },
          "phoneMasked": {
            "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "nameMasked",
          "phoneMasked"
        ],
        "type": "object"
      },
      "BookingCustomerInput": {
        "description": "Reference an existing customer by id, or supply enough to find-or-create one inline.",
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "id": {
                "maxLength": 128,
                "type": "string"
              }
            },
            "required": [
              "id"
            ],
            "type": "object"
          },
          {
            "additionalProperties": false,
            "properties": {
              "email": {
                "format": "email",
                "maxLength": 254,
                "type": "string"
              },
              "name": {
                "maxLength": 120,
                "minLength": 1,
                "type": "string"
              },
              "phone": {
                "description": "E.164 preferred.",
                "maxLength": 32,
                "minLength": 1,
                "type": "string"
              }
            },
            "required": [
              "name",
              "phone"
            ],
            "type": "object"
          }
        ]
      },
      "BookingPatchRequest": {
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "action": {
                "const": "reschedule",
                "type": "string"
              },
              "date": {
                "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                "type": "string"
              },
              "time": {
                "pattern": "^\\d{2}:\\d{2}$",
                "type": "string"
              }
            },
            "required": [
              "action",
              "date",
              "time"
            ],
            "type": "object"
          },
          {
            "additionalProperties": false,
            "properties": {
              "action": {
                "const": "cancel",
                "type": "string"
              },
              "reason": {
                "maxLength": 500,
                "type": "string"
              }
            },
            "required": [
              "action"
            ],
            "type": "object"
          }
        ]
      },
      "BookingRequest": {
        "additionalProperties": false,
        "properties": {
          "customer": {
            "description": "Reference an existing customer by id, or supply enough to find-or-create one inline.",
            "oneOf": [
              {
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "maxLength": 128,
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ],
                "type": "object"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "email": {
                    "format": "email",
                    "maxLength": 254,
                    "type": "string"
                  },
                  "name": {
                    "maxLength": 120,
                    "minLength": 1,
                    "type": "string"
                  },
                  "phone": {
                    "description": "E.164 preferred.",
                    "maxLength": 32,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "phone"
                ],
                "type": "object"
              }
            ]
          },
          "date": {
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "type": "string"
          },
          "notes": {
            "maxLength": 500,
            "type": "string"
          },
          "serviceId": {
            "maxLength": 128,
            "minLength": 1,
            "type": "string"
          },
          "time": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string"
          }
        },
        "required": [
          "serviceId",
          "date",
          "time",
          "customer"
        ],
        "type": "object"
      },
      "BookingRescheduleRequest": {
        "additionalProperties": false,
        "properties": {
          "action": {
            "const": "reschedule",
            "type": "string"
          },
          "date": {
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "type": "string"
          },
          "time": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string"
          }
        },
        "required": [
          "action",
          "date",
          "time"
        ],
        "type": "object"
      },
      "BookingResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "additionalProperties": false,
            "properties": {
              "customer": {
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "description": "Null when no customer-book row resolves for this booking's phone.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "nameMasked": {
                    "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                    "type": "string"
                  },
                  "phoneMasked": {
                    "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                    "type": "string"
                  }
                },
                "required": [
                  "id",
                  "nameMasked",
                  "phoneMasked"
                ],
                "type": "object"
              },
              "durationMinutes": {
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "serviceId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "serviceNameAr": {
                "maxLength": 200,
                "type": "string"
              },
              "serviceNameEn": {
                "maxLength": 200,
                "type": "string"
              },
              "startsAt": {
                "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
                "format": "date-time",
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "timezone": {
                "description": "IANA zone id, e.g. Asia/Qatar.",
                "type": "string"
              }
            },
            "required": [
              "id",
              "status",
              "serviceId",
              "serviceNameEn",
              "serviceNameAr",
              "startsAt",
              "durationMinutes",
              "timezone",
              "customer"
            ],
            "type": "object"
          }
        },
        "required": [
          "data"
        ],
        "type": "object"
      },
      "ChangesResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "items": {
              "additionalProperties": false,
              "properties": {
                "changedAt": {
                  "format": "date-time",
                  "type": "string"
                },
                "customer": {
                  "additionalProperties": false,
                  "properties": {
                    "id": {
                      "description": "Null when no customer-book row resolves for this booking's phone.",
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "nameMasked": {
                      "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                      "type": "string"
                    },
                    "phoneMasked": {
                      "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "nameMasked",
                    "phoneMasked"
                  ],
                  "type": "object"
                },
                "durationMinutes": {
                  "minimum": 0,
                  "type": "integer"
                },
                "id": {
                  "type": "string"
                },
                "serviceId": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "serviceNameAr": {
                  "maxLength": 200,
                  "type": "string"
                },
                "serviceNameEn": {
                  "maxLength": 200,
                  "type": "string"
                },
                "startsAt": {
                  "format": "date-time",
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "timezone": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "status",
                "serviceId",
                "serviceNameEn",
                "serviceNameAr",
                "startsAt",
                "durationMinutes",
                "timezone",
                "customer",
                "changedAt"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "hasMore": {
            "description": "true means call again immediately with `nextSince`. false means poll again later — it does not mean nothing will ever be redelivered inside the stability window.",
            "type": "boolean"
          },
          "nextSince": {
            "description": "Opaque cursor. Pass back verbatim as `since` on the next call; do not parse or construct it.",
            "type": "string"
          }
        },
        "required": [
          "data",
          "nextSince",
          "hasMore"
        ],
        "type": "object"
      },
      "Customer": {
        "additionalProperties": false,
        "properties": {
          "created": {
            "description": "true when this call inserted a new row; false when an existing one matched by phone.",
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "description": "Echoes the name THIS request supplied — never the stored name. See the reference doc's enumeration-safety note.",
            "type": "string"
          },
          "phoneMasked": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "phoneMasked",
          "created"
        ],
        "type": "object"
      },
      "CustomerRequest": {
        "additionalProperties": false,
        "properties": {
          "email": {
            "format": "email",
            "maxLength": 254,
            "type": "string"
          },
          "name": {
            "maxLength": 120,
            "minLength": 1,
            "type": "string"
          },
          "phone": {
            "maxLength": 32,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "name",
          "phone"
        ],
        "type": "object"
      },
      "CustomerResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "additionalProperties": false,
            "properties": {
              "created": {
                "description": "true when this call inserted a new row; false when an existing one matched by phone.",
                "type": "boolean"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "description": "Echoes the name THIS request supplied — never the stored name. See the reference doc's enumeration-safety note.",
                "type": "string"
              },
              "phoneMasked": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "phoneMasked",
              "created"
            ],
            "type": "object"
          }
        },
        "required": [
          "data"
        ],
        "type": "object"
      },
      "ErrorDetail": {
        "additionalProperties": false,
        "properties": {
          "code": {
            "description": "A specific, documented code within `type` (e.g. `slot_conflict`, `insufficient_scope`).",
            "type": "string"
          },
          "message": {
            "description": "Human-readable; safe to log, not guaranteed stable text to match on.",
            "type": "string"
          },
          "param": {
            "description": "The offending request field or header, when the error names one.",
            "type": "string"
          },
          "requestId": {
            "description": "Echoes X-Request-Id. Include this when reporting an issue.",
            "type": "string"
          },
          "type": {
            "description": "The error category. Stable across releases; branch on this, not on `code`.",
            "enum": [
              "invalid_request",
              "authentication",
              "permission",
              "rate_limit",
              "conflict",
              "quota",
              "server"
            ],
            "type": "string"
          }
        },
        "required": [
          "type",
          "code",
          "message",
          "requestId"
        ],
        "type": "object"
      },
      "ErrorResponse": {
        "additionalProperties": false,
        "properties": {
          "error": {
            "additionalProperties": false,
            "properties": {
              "code": {
                "description": "A specific, documented code within `type` (e.g. `slot_conflict`, `insufficient_scope`).",
                "type": "string"
              },
              "message": {
                "description": "Human-readable; safe to log, not guaranteed stable text to match on.",
                "type": "string"
              },
              "param": {
                "description": "The offending request field or header, when the error names one.",
                "type": "string"
              },
              "requestId": {
                "description": "Echoes X-Request-Id. Include this when reporting an issue.",
                "type": "string"
              },
              "type": {
                "description": "The error category. Stable across releases; branch on this, not on `code`.",
                "enum": [
                  "invalid_request",
                  "authentication",
                  "permission",
                  "rate_limit",
                  "conflict",
                  "quota",
                  "server"
                ],
                "type": "string"
              }
            },
            "required": [
              "type",
              "code",
              "message",
              "requestId"
            ],
            "type": "object"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      },
      "ProjectedBookingRefusal": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "additionalProperties": false,
            "properties": {
              "customer": {
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "description": "Null when no customer-book row resolves for this booking's phone.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "nameMasked": {
                    "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                    "type": "string"
                  },
                  "phoneMasked": {
                    "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                    "type": "string"
                  }
                },
                "required": [
                  "id",
                  "nameMasked",
                  "phoneMasked"
                ],
                "type": "object"
              },
              "durationMinutes": {
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "serviceId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "serviceNameAr": {
                "maxLength": 200,
                "type": "string"
              },
              "serviceNameEn": {
                "maxLength": 200,
                "type": "string"
              },
              "startsAt": {
                "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
                "format": "date-time",
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "timezone": {
                "description": "IANA zone id, e.g. Asia/Qatar.",
                "type": "string"
              }
            },
            "required": [
              "id",
              "status",
              "serviceId",
              "serviceNameEn",
              "serviceNameAr",
              "startsAt",
              "durationMinutes",
              "timezone",
              "customer"
            ],
            "type": "object"
          },
          "error": {
            "additionalProperties": false,
            "properties": {
              "code": {
                "description": "A specific, documented code within `type` (e.g. `slot_conflict`, `insufficient_scope`).",
                "type": "string"
              },
              "message": {
                "description": "Human-readable; safe to log, not guaranteed stable text to match on.",
                "type": "string"
              },
              "param": {
                "description": "The offending request field or header, when the error names one.",
                "type": "string"
              },
              "requestId": {
                "description": "Echoes X-Request-Id. Include this when reporting an issue.",
                "type": "string"
              },
              "type": {
                "description": "The error category. Stable across releases; branch on this, not on `code`.",
                "enum": [
                  "invalid_request",
                  "authentication",
                  "permission",
                  "rate_limit",
                  "conflict",
                  "quota",
                  "server"
                ],
                "type": "string"
              }
            },
            "required": [
              "type",
              "code",
              "message",
              "requestId"
            ],
            "type": "object"
          }
        },
        "required": [
          "error",
          "data"
        ],
        "type": "object"
      },
      "Service": {
        "additionalProperties": false,
        "properties": {
          "category": {
            "maxLength": 120,
            "type": [
              "string",
              "null"
            ]
          },
          "categoryAr": {
            "maxLength": 120,
            "type": [
              "string",
              "null"
            ]
          },
          "durationMinutes": {
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "nameAr": {
            "maxLength": 200,
            "type": "string"
          },
          "nameEn": {
            "maxLength": 200,
            "type": "string"
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "nameEn",
          "nameAr",
          "durationMinutes",
          "price",
          "category",
          "categoryAr"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "AgentApiKey": {
        "bearerFormat": "mwd_live_<key>",
        "description": "A tenant agent API key, issued from the dashboard's Agent Access settings page. Sent as `Authorization: Bearer mwd_live_...`. Revoked or expired keys and unknown keys are indistinguishable in the response (401 invalid_api_key) — the distinguishing reason is logged server-side only.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Every operation below is served under `/api/agent/v1` and authenticated with a bearer API key\n(`mwd_live_…`) minted from a tenant's dashboard. There is no cookie or session authority on this\nsurface, and no CORS headers are ever emitted — the credential is a header a browser never attaches\non its own.\n\nAll customer-identifying fields in every response are masked (see each schema's `nameMasked` /\n`phoneMasked` properties) — this API never returns a stored full name, phone number, or email\naddress. `POST /customers` is the one endpoint that echoes a name, and it echoes only the name the\nCALLER supplied on that request, never the stored one.\n\nSee `docs/agent-api-reference.md` for the authentication flow, the idempotency contract, rate-limit\nand credit behavior, and the separate MCP endpoint at `/api/agent/mcp`.",
    "summary": "REST contract for external AI agents booking and managing appointments on behalf of a Mawidi tenant.",
    "title": "Mawidi Agent Platform API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/audit/receipts": {
      "get": {
        "description": "List THIS agent client's own per-capability usage receipts, newest first, cursor-paginated. Never another client's rows, and never organization-wide — that is a separate, not-yet-shipped scope. Each receipt joins one `agent_usage_events` ledger row with the static capability that produced it: what ran, how it ended, and whether it can be priced per capability. `exportDigest` is a sha256 CONTENT digest over this page's receipts (stable key order, no whitespace) for dedupe and corruption detection — it has NO key, so it does not prove the page came from Mawidi. There is no SLA/uptime/latency reporting on this API.",
        "operationId": "listAgentReceipts",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 100, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque Convex pagination cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. Longer than 512 characters is refused with 400. A cursor that no longer matches this query (stale, corrupted, or issued to a different key) is refused with 400 rather than silently restarting from the first page.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          },
          {
            "description": "Inclusive lower bound on `occurredAt`, epoch milliseconds. Omit for no lower bound.",
            "in": "query",
            "name": "sinceCreatedAt",
            "required": false,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Exclusive upper bound on `occurredAt`, epoch milliseconds. Omit for no upper bound. Refused with 400 if not greater than `sinceCreatedAt` when both are given.",
            "in": "query",
            "name": "untilCreatedAt",
            "required": false,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "accountingWindowStart": {
                            "description": "The UTC day this interaction is accounted to, epoch milliseconds.",
                            "type": "integer"
                          },
                          "admissionMode": {
                            "enum": [
                              "observe",
                              "enforce"
                            ],
                            "type": "string"
                          },
                          "agentClientId": {
                            "type": "string"
                          },
                          "capability": {
                            "additionalProperties": false,
                            "properties": {
                              "billingUnit": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "class": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "id": {
                                "description": "Null when the gateway could not name the capability (a row predating WP-46).",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "idempotency": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "route": {
                                "description": "The literal request path this interaction reached. Embeds a resource id for a path-based alias (e.g. a booking id).",
                                "type": "string"
                              },
                              "usageKind": {
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "charge": {
                            "additionalProperties": false,
                            "properties": {
                              "billable": {
                                "type": "boolean"
                              },
                              "creditsDebited": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "duplicateOf": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "estimatedCostMicroUsd": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "quantity": {
                                "type": "number"
                              },
                              "settlementBatchId": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "unitPriceMicroUsd": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              }
                            },
                            "type": "object"
                          },
                          "environment": {
                            "type": "string"
                          },
                          "eventId": {
                            "type": "string"
                          },
                          "occurredAt": {
                            "description": "Capture time, epoch milliseconds.",
                            "type": "integer"
                          },
                          "outcome": {
                            "additionalProperties": false,
                            "properties": {
                              "code": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "dedupedExisting": {
                                "type": "boolean"
                              },
                              "httpStatus": {
                                "type": "integer"
                              },
                              "idempotencyKey": {
                                "description": "The RAW Idempotency-Key header value THIS client sent on the original request, verbatim — never hashed, never another client's.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "operationKey": {
                                "description": "A derived sha256 identity, never a caller-supplied value.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "status": {
                                "enum": [
                                  "denied",
                                  "replayed",
                                  "superseded",
                                  "not_executed",
                                  "failed",
                                  "queued",
                                  "refused",
                                  "served"
                                ],
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "receiptVersion": {
                            "const": 1,
                            "type": "integer"
                          },
                          "reconciliation": {
                            "enum": [
                              "reconcilable",
                              "capability_unknown",
                              "capability_unrecognized",
                              "billing_mismatch"
                            ],
                            "type": "string"
                          },
                          "requestId": {
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "exportDigest": {
                      "description": "sha256 CONTENT digest over this page's receipts — stable page identity and corruption detection, NOT a signature: it has no key and does not prove the page came from Mawidi.",
                      "pattern": "^[0-9a-f]{64}$",
                      "type": "string"
                    },
                    "exportVersion": {
                      "const": 1,
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque Convex pagination cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging or window input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100), `cursor` (longer than 512 characters, or not a cursor this endpoint issued), or `sinceCreatedAt`/`untilCreatedAt` (not a non-negative integer, or `untilCreatedAt` not greater than `sinceCreatedAt`). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "audit.read"
            ]
          }
        ],
        "summary": "List this API client's own usage receipts",
        "tags": [
          "Audit"
        ],
        "x-capability-ids": [
          "audit.receipts.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "audit.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.audit_export"
      }
    },
    "/availability": {
      "get": {
        "description": "Free HH:mm start times over a 1-14 day window, computed by the exact same slot grid the booking write path enforces — a slot returned here is guaranteed bookable at the moment it is offered (subject to a concurrent booking winning the race).",
        "operationId": "getAvailability",
        "parameters": [
          {
            "in": "query",
            "name": "serviceId",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "First calendar date of the window, business-local.",
            "in": "query",
            "name": "date",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "type": "string"
            }
          },
          {
            "description": "Window length. Defaults to 1.",
            "in": "query",
            "name": "days",
            "required": false,
            "schema": {
              "default": 1,
              "maximum": 14,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "days": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "date": {
                            "description": "YYYY-MM-DD, business-local calendar date.",
                            "format": "date",
                            "type": "string"
                          },
                          "slots": {
                            "items": {
                              "description": "HH:mm business-local wall clock.",
                              "pattern": "^\\d{2}:\\d{2}$",
                              "type": "string"
                            },
                            "type": "array"
                          }
                        },
                        "required": [
                          "date",
                          "slots"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "timezone": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "timezone",
                    "days"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No active service with that id for this tenant.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "availability.read"
            ]
          }
        ],
        "summary": "Get open slots for one service",
        "tags": [
          "Availability"
        ],
        "x-capability-ids": [
          "availability.search"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentRead",
          "limit": 120,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "availability.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.availability"
      }
    },
    "/bookings": {
      "get": {
        "description": "Bookings whose start falls within from..to (both YYYY-MM-DD, inclusive, business-local calendar days), newest-index-order first, optionally narrowed to one status. The window may span at most 31 days. Own-created by default (`bookings.read`); `bookings.read.org` widens that to every booking in the organization, staff edits included — the identical scope pair `bookings.get` uses.",
        "operationId": "listBookings",
        "parameters": [
          {
            "description": "Inclusive lower bound, YYYY-MM-DD, business-local calendar date.",
            "in": "query",
            "name": "from",
            "required": true,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Inclusive upper bound, YYYY-MM-DD, business-local calendar date. Must not be before from.",
            "in": "query",
            "name": "to",
            "required": true,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Optional filter to one booking status. Omit to include every status.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "pending",
                "confirmed",
                "cancelled",
                "completed",
                "no_show"
              ],
              "type": "string"
            }
          },
          {
            "description": "Rows per page; defaults to 50, capped at 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Opaque page cursor from a previous response's `nextCursor`. Omit to fetch the first page.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "customer": {
                            "additionalProperties": false,
                            "properties": {
                              "id": {
                                "description": "Null when no customer-book row resolves for this booking's phone.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "nameMasked": {
                                "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                                "type": "string"
                              },
                              "phoneMasked": {
                                "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                                "type": "string"
                              }
                            },
                            "required": [
                              "id",
                              "nameMasked",
                              "phoneMasked"
                            ],
                            "type": "object"
                          },
                          "durationMinutes": {
                            "minimum": 0,
                            "type": "integer"
                          },
                          "id": {
                            "type": "string"
                          },
                          "serviceId": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "serviceNameAr": {
                            "maxLength": 200,
                            "type": "string"
                          },
                          "serviceNameEn": {
                            "maxLength": 200,
                            "type": "string"
                          },
                          "startsAt": {
                            "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
                            "format": "date-time",
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "timezone": {
                            "description": "IANA zone id, e.g. Asia/Qatar.",
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "status",
                          "serviceId",
                          "serviceNameEn",
                          "serviceNameAr",
                          "startsAt",
                          "durationMinutes",
                          "timezone",
                          "customer"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque page cursor. Pass back verbatim as `cursor` on the next call; null on the last page.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor",
                    "hasMore"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "bookings.read"
            ]
          },
          {
            "AgentApiKey": [
              "bookings.read.org"
            ]
          }
        ],
        "summary": "List bookings in a date range",
        "tags": [
          "Bookings"
        ],
        "x-capability-ids": [
          "bookings.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentRead",
          "limit": 120,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "bookings.read",
          "bookings.read.org"
        ],
        "x-scope-mode": "deferred",
        "x-usage-kind": "read.bookings_list"
      },
      "post": {
        "description": "Creates a pending booking. The slot's three gates (future-only, business hours, no conflict) run inside one transaction with the insert, so this is the sole concurrency control for creates — Idempotency-Key stops one caller's retry from double-booking, it does not arbitrate two different callers racing for the same slot.",
        "operationId": "createBooking",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "customer": {
                    "description": "Reference an existing customer by id, or supply enough to find-or-create one inline.",
                    "oneOf": [
                      {
                        "additionalProperties": false,
                        "properties": {
                          "id": {
                            "maxLength": 128,
                            "type": "string"
                          }
                        },
                        "required": [
                          "id"
                        ],
                        "type": "object"
                      },
                      {
                        "additionalProperties": false,
                        "properties": {
                          "email": {
                            "format": "email",
                            "maxLength": 254,
                            "type": "string"
                          },
                          "name": {
                            "maxLength": 120,
                            "minLength": 1,
                            "type": "string"
                          },
                          "phone": {
                            "description": "E.164 preferred.",
                            "maxLength": 32,
                            "minLength": 1,
                            "type": "string"
                          }
                        },
                        "required": [
                          "name",
                          "phone"
                        ],
                        "type": "object"
                      }
                    ]
                  },
                  "date": {
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                    "type": "string"
                  },
                  "notes": {
                    "maxLength": 500,
                    "type": "string"
                  },
                  "serviceId": {
                    "maxLength": 128,
                    "minLength": 1,
                    "type": "string"
                  },
                  "time": {
                    "pattern": "^\\d{2}:\\d{2}$",
                    "type": "string"
                  }
                },
                "required": [
                  "serviceId",
                  "date",
                  "time",
                  "customer"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "customer": {
                          "additionalProperties": false,
                          "properties": {
                            "id": {
                              "description": "Null when no customer-book row resolves for this booking's phone.",
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "nameMasked": {
                              "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                              "type": "string"
                            },
                            "phoneMasked": {
                              "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                              "type": "string"
                            }
                          },
                          "required": [
                            "id",
                            "nameMasked",
                            "phoneMasked"
                          ],
                          "type": "object"
                        },
                        "durationMinutes": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "id": {
                          "type": "string"
                        },
                        "serviceId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "serviceNameAr": {
                          "maxLength": 200,
                          "type": "string"
                        },
                        "serviceNameEn": {
                          "maxLength": 200,
                          "type": "string"
                        },
                        "startsAt": {
                          "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "timezone": {
                          "description": "IANA zone id, e.g. Asia/Qatar.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "status",
                        "serviceId",
                        "serviceNameEn",
                        "serviceNameAr",
                        "startsAt",
                        "durationMinutes",
                        "timezone",
                        "customer"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Created (or replayed from an idempotency key already bound to this exact request).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No active service, or no customer, with the given id for this tenant.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The same Idempotency-Key was already used for a different request body (409 idempotency_key_reuse), or an identical request under this key is still in flight (409 request_in_progress, with Retry-After).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The request is well-formed but the target state is not reachable right now (e.g. the requested slot is in the past, outside business hours, or not on the offered grid; or a phone could not be resolved).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "bookings.write"
            ]
          }
        ],
        "summary": "Create a booking",
        "tags": [
          "Bookings"
        ],
        "x-capability-ids": [
          "bookings.create"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "bookings.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.booking_created"
      }
    },
    "/bookings/changes": {
      "get": {
        "description": "A snapshot feed delivered at-least-once: every row carries a booking's CURRENT state, not a diff, so apply on `(id, updatedAt)` with `>=` semantics — never skip a delivery whose `updatedAt` you have already seen, because two edits inside the same millisecond share a delivery key while carrying different states. `hasMore: true` means call again immediately; `false` means poll again later, not that nothing will be redelivered — `nextSince` deliberately lags behind a 5-second stability watermark so an edit landing near the page boundary is re-read on the next poll rather than silently missed.",
        "operationId": "listBookingChanges",
        "parameters": [
          {
            "description": "An opaque cursor from a previous response's `nextSince`. Omit for the first page.",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page size. Defaults to 100, the maximum.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "changedAt": {
                            "format": "date-time",
                            "type": "string"
                          },
                          "customer": {
                            "additionalProperties": false,
                            "properties": {
                              "id": {
                                "description": "Null when no customer-book row resolves for this booking's phone.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "nameMasked": {
                                "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                                "type": "string"
                              },
                              "phoneMasked": {
                                "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                                "type": "string"
                              }
                            },
                            "required": [
                              "id",
                              "nameMasked",
                              "phoneMasked"
                            ],
                            "type": "object"
                          },
                          "durationMinutes": {
                            "minimum": 0,
                            "type": "integer"
                          },
                          "id": {
                            "type": "string"
                          },
                          "serviceId": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "serviceNameAr": {
                            "maxLength": 200,
                            "type": "string"
                          },
                          "serviceNameEn": {
                            "maxLength": 200,
                            "type": "string"
                          },
                          "startsAt": {
                            "format": "date-time",
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "timezone": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "status",
                          "serviceId",
                          "serviceNameEn",
                          "serviceNameAr",
                          "startsAt",
                          "durationMinutes",
                          "timezone",
                          "customer",
                          "changedAt"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "true means call again immediately with `nextSince`. false means poll again later — it does not mean nothing will ever be redelivered inside the stability window.",
                      "type": "boolean"
                    },
                    "nextSince": {
                      "description": "Opaque cursor. Pass back verbatim as `since` on the next call; do not parse or construct it.",
                      "type": "string"
                    }
                  },
                  "required": [
                    "data",
                    "nextSince",
                    "hasMore"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "bookings.read"
            ]
          },
          {
            "AgentApiKey": [
              "bookings.read.org"
            ]
          }
        ],
        "summary": "Pull the booking change feed",
        "tags": [
          "Bookings"
        ],
        "x-capability-ids": [
          "bookings.changes.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "bookings.read",
          "bookings.read.org"
        ],
        "x-scope-mode": "deferred",
        "x-usage-kind": "read.booking_changes"
      }
    },
    "/bookings/{id}": {
      "get": {
        "description": "Own-created by default: the `bookings.read` scope sees only bookings this API client created. The `bookings.read.org` scope widens that to every booking in the organization, including ones staff created.",
        "operationId": "getBooking",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "customer": {
                          "additionalProperties": false,
                          "properties": {
                            "id": {
                              "description": "Null when no customer-book row resolves for this booking's phone.",
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "nameMasked": {
                              "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                              "type": "string"
                            },
                            "phoneMasked": {
                              "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                              "type": "string"
                            }
                          },
                          "required": [
                            "id",
                            "nameMasked",
                            "phoneMasked"
                          ],
                          "type": "object"
                        },
                        "durationMinutes": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "id": {
                          "type": "string"
                        },
                        "serviceId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "serviceNameAr": {
                          "maxLength": 200,
                          "type": "string"
                        },
                        "serviceNameEn": {
                          "maxLength": 200,
                          "type": "string"
                        },
                        "startsAt": {
                          "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "timezone": {
                          "description": "IANA zone id, e.g. Asia/Qatar.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "status",
                        "serviceId",
                        "serviceNameEn",
                        "serviceNameAr",
                        "startsAt",
                        "durationMinutes",
                        "timezone",
                        "customer"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No such resource for this tenant. Also returned for a foreign tenant's id or another client's booking — by design, never a 403, so this surface cannot be used to confirm an id exists elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "bookings.read"
            ]
          },
          {
            "AgentApiKey": [
              "bookings.read.org"
            ]
          }
        ],
        "summary": "Get one booking",
        "tags": [
          "Bookings"
        ],
        "x-capability-ids": [
          "bookings.get"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentRead",
          "limit": 120,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "bookings.read",
          "bookings.read.org"
        ],
        "x-scope-mode": "deferred",
        "x-usage-kind": "read.booking"
      },
      "patch": {
        "description": "Always own-created — there is no organization-wide write scope. Body is `{ \"action\": \"reschedule\", date, time }` or `{ \"action\": \"cancel\", reason? }`. A booking that is no longer active (already completed, no-show, or cancelled for reschedule) is refused with 409 and the schema below, which carries the booking's current state alongside the error so the caller does not need a second round trip to learn why.",
        "operationId": "updateBooking",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "additionalProperties": false,
                    "properties": {
                      "action": {
                        "const": "reschedule",
                        "type": "string"
                      },
                      "date": {
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                        "type": "string"
                      },
                      "time": {
                        "pattern": "^\\d{2}:\\d{2}$",
                        "type": "string"
                      }
                    },
                    "required": [
                      "action",
                      "date",
                      "time"
                    ],
                    "type": "object"
                  },
                  {
                    "additionalProperties": false,
                    "properties": {
                      "action": {
                        "const": "cancel",
                        "type": "string"
                      },
                      "reason": {
                        "maxLength": 500,
                        "type": "string"
                      }
                    },
                    "required": [
                      "action"
                    ],
                    "type": "object"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "customer": {
                          "additionalProperties": false,
                          "properties": {
                            "id": {
                              "description": "Null when no customer-book row resolves for this booking's phone.",
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "nameMasked": {
                              "description": "e.g. \"Ah… A.\" — at most 3 leading code points of the first word, plus the last word's initial.",
                              "type": "string"
                            },
                            "phoneMasked": {
                              "description": "e.g. \"•••••123\" — at most the last 3 digits, fixed-width prefix.",
                              "type": "string"
                            }
                          },
                          "required": [
                            "id",
                            "nameMasked",
                            "phoneMasked"
                          ],
                          "type": "object"
                        },
                        "durationMinutes": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "id": {
                          "type": "string"
                        },
                        "serviceId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "serviceNameAr": {
                          "maxLength": 200,
                          "type": "string"
                        },
                        "serviceNameEn": {
                          "maxLength": 200,
                          "type": "string"
                        },
                        "startsAt": {
                          "description": "ISO-8601 with the business timezone's own UTC offset, e.g. 2026-09-05T09:00:00+03:00 — never normalized to UTC.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "timezone": {
                          "description": "IANA zone id, e.g. Asia/Qatar.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "status",
                        "serviceId",
                        "serviceNameEn",
                        "serviceNameAr",
                        "startsAt",
                        "durationMinutes",
                        "timezone",
                        "customer"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Rescheduled or cancelled.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No such resource for this tenant. Also returned for a foreign tenant's id or another client's booking — by design, never a 403, so this surface cannot be used to confirm an id exists elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ProjectedBookingRefusal"
                    }
                  ]
                }
              }
            },
            "description": "Either an idempotency conflict (see the shared 409 description), a slot no longer available on reschedule (slot_conflict), or the booking's current status does not allow this action (booking_not_reschedulable / booking_not_cancellable — this variant's body carries `data`, the booking's current state, alongside `error`).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The request is well-formed but the target state is not reachable right now (e.g. the requested slot is in the past, outside business hours, or not on the offered grid; or a phone could not be resolved).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "bookings.write"
            ]
          }
        ],
        "summary": "Reschedule or cancel a booking",
        "tags": [
          "Bookings"
        ],
        "x-capability-ids": [
          "bookings.reschedule",
          "bookings.cancel"
        ],
        "x-idempotent": true,
        "x-possible-usage-kinds": [
          "write.booking_rescheduled",
          "write.booking_cancelled"
        ],
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "bookings.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "dynamic"
      }
    },
    "/config/business-hours": {
      "get": {
        "description": "This tenant's weekly opening-hours template, one row per configured weekday. `slotDuration`/`bufferTime`/`timezone` are read here but are NOT arguments `POST /config/business-hours` accepts — a written weekday INHERITS them, since changing a slot length is a booking-capacity decision, not an opening-hours one. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listBusinessHours",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "bufferTime": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "dayOfWeek": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "endTime": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "isActive": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "slotDuration": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "startTime": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "timezone": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "business_hours.read"
            ]
          }
        ],
        "summary": "List business hours",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.business_hours.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "business_hours.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.config_business_hours"
      },
      "post": {
        "description": "Replaces ONE weekday's opening-hours window whole — `dayOfWeek`, `startTime`, `endTime` and `isActive` are absolute values, not a diff. A weekday with more than one configured window (e.g. a lunch-break split), or a week whose rows disagree about timezone, is refused with `409 business_hours_ambiguous` rather than collapsed into one window — replacing it here would silently delete opening hours a person configured. The written row INHERITS its slot length and buffer from the day it replaces, or from the rest of the week when it agrees: neither is an argument here, since changing them is a booking-capacity decision, not an opening-hours one. Always `200`, never `201` — a weekday is a slot in a fixed weekly template, so this write converges on one row per `(organization, dayOfWeek)` whether it had to be inserted or only patched, and `data.changed` answers whether anything moved.",
        "operationId": "setBusinessHours",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "dayOfWeek": {
                    "description": "0 = Sunday .. 6 = Saturday. One call replaces this weekday's window whole.",
                    "maximum": 6,
                    "minimum": 0,
                    "type": "integer"
                  },
                  "endTime": {
                    "description": "Must be later than startTime.",
                    "pattern": "^\\d{2}:\\d{2}$",
                    "type": "string"
                  },
                  "isActive": {
                    "description": "Required, unlike everywhere else on this surface: the write replaces a weekday outright, so omitting it would choose \"open\" or \"closed\" on the caller's behalf.",
                    "type": "boolean"
                  },
                  "startTime": {
                    "description": "Wall-clock HH:mm in the business timezone.",
                    "pattern": "^\\d{2}:\\d{2}$",
                    "type": "string"
                  }
                },
                "required": [
                  "dayOfWeek",
                  "startTime",
                  "endTime",
                  "isActive"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "availabilityId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "changed": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The weekday's window now matches the request. `data.changed` is `false` when a rerun matched a row already in this exact state.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either malformed input (`error.param` names dayOfWeek, startTime or endTime) or 400 invalid_hours (`param: body`) — the window is not one the slot engine's own clock parser and shape validator accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either an idempotency conflict at the HTTP claim-store layer (idempotency_key_reuse, or request_in_progress with Retry-After), or 409 business_hours_ambiguous (param: dayOfWeek) — this weekday has more than one configured window, or the week's timezones disagree.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "business_hours.write"
            ]
          }
        ],
        "summary": "Set one weekday's business hours",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.business_hours.set"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "business_hours.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.config_business_hours_set"
      }
    },
    "/config/knowledge": {
      "get": {
        "description": "This tenant's WhatsApp AI knowledge base — question/answer pairs the AI reads from when replying to customers. `answer` is returned in full (bounded, not previewed), so this is how an agent audits what its tenant's AI is telling customers. `category` and `priority` are read-only: they are the tenant's own ranking of its knowledge base, and an agent that could raise an entry's priority could rank its own answer above the owner's. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listKnowledgeEntries",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "answer": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "category": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "isActive": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "language": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "priority": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "question": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "knowledge.read"
            ]
          }
        ],
        "summary": "List knowledge-base entries",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.knowledge.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "knowledge.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.config_knowledge"
      },
      "post": {
        "description": "Creates a Q/A entry (`entryId` omitted) or updates this tenant's existing one (`entryId` present) in the WhatsApp AI knowledge base `GET /config/knowledge` reads. `category` and `priority` cannot be set here — they are the tenant's own ranking of its knowledge base. Responds `201` when a new entry was inserted and `200` when an existing row was updated or an idempotency replay matched; `data.created` carries that same bit. Returns a receipt (`data.entryId`, `data.created`), never the row.",
        "operationId": "upsertKnowledgeEntry",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "answer": {
                    "maxLength": 4000,
                    "minLength": 1,
                    "type": "string"
                  },
                  "entryId": {
                    "description": "Omit to create a new entry. Present to update this tenant's existing one by id — a foreign or unknown id is refused with 404 knowledge_entry_not_found.",
                    "maxLength": 128,
                    "type": "string"
                  },
                  "isActive": {
                    "type": "boolean"
                  },
                  "language": {
                    "description": "Case-insensitive; normalized to lowercase before storage.",
                    "enum": [
                      "en",
                      "ar"
                    ],
                    "type": "string"
                  },
                  "question": {
                    "maxLength": 500,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "language",
                  "question",
                  "answer"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "entryId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An existing row was updated (`entryId` present), or a create-path idempotency replay matched (`data.created` is still `true`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "entryId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A new entry was inserted. `data.created` is `true`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either malformed input (`error.param` names the offending field) or 400 invalid_knowledge (`param: body`) — the question or answer is outside the bounds this API stores, or contains control, zero-width, or text-direction characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 knowledge_entry_not_found (param: entryId) — no knowledge entry with that id for this tenant. Also returned for another tenant's entry id, by design, never a 403.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either an idempotency conflict at the HTTP claim-store layer (idempotency_key_reuse, or request_in_progress with Retry-After), or 409 idempotency_conflict from the mutation's own claim underneath it — same conflict type, a distinct code and message (\"That idempotency key was already used for a different request.\").",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "knowledge.write"
            ]
          }
        ],
        "summary": "Create or update a knowledge-base entry",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.knowledge.upsert"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "knowledge.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.config_knowledge_upserted"
      }
    },
    "/config/services": {
      "get": {
        "description": "This tenant's service catalogue for MANAGEMENT, not booking: unlike `GET /services`, it includes inactive rows and the `isActive`/`sortOrder` fields that make it one. Not a widening of `GET /services` and not a second row on one Convex target — `GET /services` answers \"what can be booked\" under a contract shipped integrators already depend on; this answers \"what is configured\", including rows the tenant switched off. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listServiceConfig",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "category": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "categoryAr": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "durationMinutes": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "isActive": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "itemType": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "nameAr": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "nameEn": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "price": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "sortOrder": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "services.read"
            ]
          }
        ],
        "summary": "List service configuration (management view)",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.services.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "services.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.config_services"
      },
      "post": {
        "description": "Creates a service (`serviceId` omitted) or updates this tenant's existing one (`serviceId` present) in the MANAGEMENT catalogue `GET /config/services` reads. There is no delete: set `isActive` to `false` to withdraw a service instead. Responds `201` when a new service was inserted and `200` when an existing row was updated or an idempotency replay matched; `data.created` carries that same bit. Returns a receipt (`data.serviceId`, `data.created`), never the row — echoing the request back would make this endpoint a mirror an agent could use to launder its own input as tenant data.",
        "operationId": "upsertServiceConfig",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "category": {
                    "maxLength": 120,
                    "type": "string"
                  },
                  "categoryAr": {
                    "maxLength": 120,
                    "type": "string"
                  },
                  "durationMinutes": {
                    "description": "Minutes. The same ceiling GET /services filters bookable rows on (`MAX_BOOKABLE_DURATION_MINUTES`) — a service written outside it would be invisible to every agent surface, including this one.",
                    "maximum": 1439,
                    "minimum": 1,
                    "type": "integer"
                  },
                  "isActive": {
                    "description": "There is no delete on this surface: a catalogue row is referenced by historical bookings, so deactivate (false) rather than remove.",
                    "type": "boolean"
                  },
                  "nameAr": {
                    "description": "May be set to an empty string to clear it.",
                    "maxLength": 200,
                    "type": "string"
                  },
                  "nameEn": {
                    "maxLength": 200,
                    "minLength": 1,
                    "type": "string"
                  },
                  "price": {
                    "description": "Major units. May be fractional, unlike every integer property above and below.",
                    "maximum": 1000000,
                    "minimum": 0,
                    "type": "number"
                  },
                  "serviceId": {
                    "description": "Omit to create a new service. Present to update this tenant's existing one by id — a foreign or unknown id is refused with 404 service_not_found.",
                    "maxLength": 128,
                    "type": "string"
                  },
                  "sortOrder": {
                    "maximum": 100000,
                    "minimum": 0,
                    "type": "integer"
                  }
                },
                "required": [
                  "nameEn",
                  "durationMinutes"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "serviceId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An existing row was updated (`serviceId` present), or a create-path idempotency replay matched (`data.created` is still `true` — this IS the create the caller asked for, re-answered).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "serviceId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A new service was inserted. `data.created` is `true`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either malformed input (`error.param` names the offending field) or 400 invalid_service (`param: body`) — the service is outside the bounds this API stores: a name too long, a duration outside 1-1439 minutes, or a price/sortOrder out of range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 service_not_found (param: serviceId) — no service with that id for this tenant. Also returned for another tenant's service id, by design, never a 403, so this surface cannot be used to confirm a service id exists elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either an idempotency conflict at the HTTP claim-store layer (idempotency_key_reuse, or request_in_progress with Retry-After), or 409 idempotency_conflict from the mutation's own claim underneath it — same conflict type, a distinct code and message (\"That idempotency key was already used for a different request.\").",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "services.write"
            ]
          }
        ],
        "summary": "Create or update a service",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.services.upsert"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "services.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.config_service_upserted"
      }
    },
    "/config/settings": {
      "get": {
        "description": "The eleven organization settings an agent may read, out of a much larger row: `name`, `industry`, `sector`, the four greeting columns, and three READ-ONLY fields (`autoGreetingEnabled`, `depositRequired`, `depositPercentage` — visible here, not accepted by `POST /config/settings`). Every credential, every Stripe/Clerk/WhatsApp/Twilio handle, the agent platform's own kill switch, and `dataRetentionDays` are withheld outright — never returned at any scope.",
        "operationId": "getOrgSettings",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "aiGreetingAr": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "aiGreetingEn": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "autoGreetingEnabled": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "depositPercentage": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "depositRequired": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "greetingMessageAr": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "greetingMessageEn": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "id": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "industry": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "name": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "sector": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 organization_not_found (no param) — no organization settings are available for this key. The organization named is always the one the caller's own key belongs to, so this can never be used to probe another tenant's existence.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "org_settings.read"
            ]
          }
        ],
        "summary": "Get organization settings",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.settings.get"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "org_settings.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.config_settings"
      },
      "post": {
        "description": "Patches one or more of the six settable columns (`name`, `industry`, `greetingMessageEn`, `greetingMessageAr`, `aiGreetingEn`, `aiGreetingAr`) — the only ones this API may change, out of a much larger row. Naming any other field is refused with `400 invalid_request` rather than silently ignored, so an attempted prohibited write reads as an attempt, not as success. At least one field is required; an empty effective patch is `400 invalid_settings`. Every field but `name` may be set to an empty string to clear it. Returns a receipt (`data.organizationId`, `data.updatedFields`) naming exactly which columns moved, never the row. Idempotent without a claim: the patch carries absolute values, so a retry writes the same bytes and returns the same receipt — always `200`, never `201`.",
        "operationId": "updateOrgSettings",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "aiGreetingAr": {
                    "maxLength": 1000,
                    "type": "string"
                  },
                  "aiGreetingEn": {
                    "maxLength": 1000,
                    "type": "string"
                  },
                  "greetingMessageAr": {
                    "maxLength": 1000,
                    "type": "string"
                  },
                  "greetingMessageEn": {
                    "maxLength": 1000,
                    "type": "string"
                  },
                  "industry": {
                    "maxLength": 120,
                    "type": "string"
                  },
                  "name": {
                    "description": "May never be emptied — the column is required. Every other field below may be set to an empty string to clear it.",
                    "maxLength": 200,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "organizationId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "updatedFields": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either malformed input — `error.param` names a field outside the six settable columns; naming `dataRetentionDays`, a Stripe/Clerk/WhatsApp credential, or the agent platform's own kill switch is refused rather than silently ignored, so an attempted prohibited write reads as an attempt — or 400 invalid_settings (`param: body`) when every named field is settable but the effective patch is empty, or a value is out of bounds.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The same Idempotency-Key was already used for a different request body (409 idempotency_key_reuse), or an identical request under this key is still in flight (409 request_in_progress, with Retry-After).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "org_settings.write"
            ]
          }
        ],
        "summary": "Update organization settings",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.settings.update"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "org_settings.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.config_settings_updated"
      }
    },
    "/config/voice-agent": {
      "get": {
        "description": "A derived summary of this tenant's voice agent, never the row itself. `data.configured` is `false` (every other field `null`/empty) for a tenant with no voice agent set up — a legitimate state, answered as a body, never a 404, and the same body covers an organization that does not resolve and an ambiguous legacy owner alias, so three states give one answer and none of them is an oracle. `systemPrompt` itself is withheld outright — `systemPromptPresent`/`systemPromptLength` say whether one is configured and roughly how elaborate, never the text, which is the tenant's own competitive asset. Every security control (`postCallWebhookUrl`, `domainAllowlist`, `signedUrlEnabled`) and every provider handle is withheld too.",
        "operationId": "getVoiceAgentConfig",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "agentEnabled": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "configured": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "enabledToolIds": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "pauseReason": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "primaryLanguage": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "setupSectorId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "systemPromptLength": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "systemPromptPresent": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "tonePreference": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "updatedAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK — always, whether or not a voice agent is configured (see `data.configured`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "voice_agent.read"
            ]
          }
        ],
        "summary": "Get voice-agent configuration",
        "tags": [
          "Config"
        ],
        "x-capability-ids": [
          "config.voice_agent.get"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "voice_agent.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.config_voice_agent"
      }
    },
    "/conversations": {
      "get": {
        "description": "One page of conversation THREAD HEADERS — status, language, message count, human-controlled flag and timestamps. The participant's name and phone are masked. There is NO message content: `lastMessage` is not projected — a redacted preview of it was built and removed after security review, because a customer's own name typed in a variant spelling from the one stored on the row defeats any per-row redaction — and the `conversationState` scratch column is never projected either, since it has held partial orders and customer details and nothing can say in advance what is in it. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listConversations",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "createdAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "humanControlled": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "language": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "lastMessageAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "messageCount": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "participant": {
                            "additionalProperties": false,
                            "properties": {
                              "nameMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              },
                              "phoneMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "status": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "conversations.read"
            ]
          }
        ],
        "summary": "List conversation threads",
        "tags": [
          "Conversations"
        ],
        "x-capability-ids": [
          "conversations.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "conversations.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.conversations"
      }
    },
    "/conversations/{id}/replies": {
      "post": {
        "description": "Proposes ONE plain-text WhatsApp reply in an existing conversation (an id from GET /conversations). NOTHING IS SENT BY THIS CALL: it answers 202 with a reviewId, and the reply is sent once, exactly as written, only after an OWNER or ADMIN approves it under Settings -> Agent access -> Reviews. The approval card is built from Mawidi's records — recipient name and number, the business's sending number, when the customer last wrote — with the reply shown separately as agent-written text. After sending, the transcript row is written with role `staff` and the conversation is handed to a human, so the AI receptionist pauses. Twilio businesses on their own number only. Refused BEFORE any review exists (409 unless noted): 404 target_not_found (missing or another tenant's conversation — identical answers), conversation_closed, recipient_opted_out, no_inbound_message, whatsapp_window_closed (the customer last wrote over 24 hours ago, or the window closes within 5 minutes), whatsapp_sender_unavailable, whatsapp_sender_not_own (the store sends on its company's shared number), review_already_open (a reply for this conversation is already waiting), review_cap_reached (50 per business or 3 per conversation per rolling 24 hours). Credits are charged only when an approved reply is actually sent; a send whose outcome is unknown is never retried or charged. The `messages.send` scope is in no bundle and must be granted on the key explicitly.",
        "operationId": "proposeWhatsAppReply",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "text": {
                    "description": "The reply, sent exactly as written (trimmed) once the owner approves it. 1-500 characters (Unicode code points). No links or web addresses, no control characters (TAB and newline are allowed), no bidi override or isolate characters.",
                    "maxLength": 500,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "text"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "conversationId": {
                          "type": "string"
                        },
                        "messageId": {
                          "description": "The transcript row, or null when the message was sent but could not be recorded.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "recorded": {
                          "description": "False when the message WAS sent but Mawidi's transcript record of it is incomplete. Never resend.",
                          "type": "boolean"
                        },
                        "status": {
                          "enum": [
                            "sent"
                          ],
                          "type": "string"
                        }
                      },
                      "required": [
                        "conversationId",
                        "status",
                        "recorded"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A retry after the review was approved and the reply sent, once the 24-hour idempotency record has aged out: the committed receipt.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "expiresAt": {
                      "description": "Epoch ms after which the review can no longer be approved: 72 hours, or 10 minutes before the customer's 24-hour WhatsApp window closes, whichever is first.",
                      "type": "integer"
                    },
                    "reviewId": {
                      "description": "The review the owner decides on. Nothing has been sent.",
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "pending",
                        "approved"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "reviewId",
                    "status"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Queued for the owner's review. Not sent.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input: `error.param` is `text` (missing, over 500 characters, a link, a control or bidi-override character) or `conversationId`. Nothing is queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 target_not_found — no such conversation for this tenant. Also returned for another tenant's conversation, by design.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Refused before any review exists — `error.code` is one of conversation_closed, recipient_opted_out, no_inbound_message, whatsapp_window_closed, whatsapp_sender_unavailable, whatsapp_sender_not_own, review_already_open, review_cap_reached — or a retry of a review that was declined (review_rejected) or approved but could not run (review_failed), or an Idempotency-Key conflict.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "410 review_expired — a retry of a review that expired before the owner decided.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "messages.send"
            ]
          }
        ],
        "summary": "Propose a WhatsApp reply (sent only after owner approval)",
        "tags": [
          "Messages"
        ],
        "x-capability-ids": [
          "messages.whatsapp.reply"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "messages.send"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.whatsapp_reply_sent"
      }
    },
    "/customers": {
      "post": {
        "description": "200 when the phone already belonged to this tenant's customer book, 201 when a row was inserted (`data.created` carries the same bit so a caller need not branch on the status line). Never patches an existing row — `data.name` echoes what THIS request sent, never the stored name, so this endpoint cannot be used to look up whose phone number one is.",
        "operationId": "upsertCustomer",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "email": {
                    "format": "email",
                    "maxLength": 254,
                    "type": "string"
                  },
                  "name": {
                    "maxLength": 120,
                    "minLength": 1,
                    "type": "string"
                  },
                  "phone": {
                    "maxLength": 32,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "phone"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "true when this call inserted a new row; false when an existing one matched by phone.",
                          "type": "boolean"
                        },
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "description": "Echoes the name THIS request supplied — never the stored name. See the reference doc's enumeration-safety note.",
                          "type": "string"
                        },
                        "phoneMasked": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "name",
                        "phoneMasked",
                        "created"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "An existing customer already had this phone number.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "true when this call inserted a new row; false when an existing one matched by phone.",
                          "type": "boolean"
                        },
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "description": "Echoes the name THIS request supplied — never the stored name. See the reference doc's enumeration-safety note.",
                          "type": "string"
                        },
                        "phoneMasked": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "name",
                        "phoneMasked",
                        "created"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A new customer row was created.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either an idempotency conflict, or 409 customer_unavailable — the phone belongs to another organization under the same owner account, so it is neither usable nor creatable here.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "422 invalid_customer_phone — the phone could not be resolved for this business. Send it in E.164 form.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "customers.write"
            ]
          }
        ],
        "summary": "Find or create a customer by phone",
        "tags": [
          "Customers"
        ],
        "x-capability-ids": [
          "customers.upsert"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentCustomerWrite",
          "limit": 30,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "customers.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.customer_upserted"
      }
    },
    "/customers/list": {
      "get": {
        "description": "One page of the tenant's customer book. Names and phone numbers are masked — this endpoint lists who the tenant already has on file, it does not reveal their contact details. Served at `/customers/list` rather than `/customers` because the shipped `POST /customers` route file would answer a GET with Next.js's own 405 before the dispatcher ever saw it. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listCustomers",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "createdAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "nameMasked": {
                            "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                            "type": "string"
                          },
                          "phoneMasked": {
                            "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                            "type": "string"
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "customers.read"
            ]
          }
        ],
        "summary": "List the customer book",
        "tags": [
          "Customers"
        ],
        "x-capability-ids": [
          "customers.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "customers.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.customers"
      }
    },
    "/leads": {
      "get": {
        "description": "One page of the tenant's leads, masked the same way `GET /customers/list` masks a customer: `contact.nameMasked`/`contact.phoneMasked`, never a raw name, phone or email. `source`, `companyName` and each `tags[]` entry are short tenant-authored labels, sanitised and bounded — never the unbounded `notes`/`interest`/`customFields`/`metadata` columns, which no capability on this surface projects at any scope. `stageId` is the lead's `pipelineStageId` under an agent-facing name; cross-reference `GET /pipeline/stages` for a stage's `isWon`/`isLost` flags before attempting `POST /leads/{id}/stage`. Tenancy is organization-keyed, not owner-keyed: a lead filed under a non-owner staff user inside this organization is still listed here — a superset within one tenant, never a cross-tenant read. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listLeads",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "companyName": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "contact": {
                            "additionalProperties": false,
                            "properties": {
                              "nameMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              },
                              "phoneMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "convertedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "createdAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "firstTouchAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "lastActivityAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "qualifiedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "score": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "source": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "sourceChannel": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "stageId": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "status": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "tags": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "touchCount": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.read"
            ]
          }
        ],
        "summary": "List leads",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.leads"
      },
      "post": {
        "description": "Creates a lead, or dedups onto an existing one, through the SAME intake (`upsertLeadFromAnyChannelInMutation`) every other channel uses — handle, then phone, then email, per owner and organization. Only `name` is required. Responds 201 when a new lead was inserted, 200 when this call matched an existing one (`data.created` carries the same bit, so a caller need not branch on the status line). This call schedules NO outbound message of any kind — no nurture enrolment, no auto-qualification, no acknowledgement send — and returns a receipt (`leadId`, `created`), never the row: returning the created lead would put an unmasked name one forgotten allowlist entry from the wire.",
        "operationId": "createLead",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "companyName": {
                    "maxLength": 120,
                    "minLength": 1,
                    "type": "string"
                  },
                  "email": {
                    "format": "email",
                    "maxLength": 254,
                    "type": "string"
                  },
                  "name": {
                    "maxLength": 120,
                    "minLength": 1,
                    "type": "string"
                  },
                  "phone": {
                    "description": "E.164 preferred. Optional, unlike POST /customers: a lead is a person who has not identified themselves yet, and the intake's dedup order (handle, then phone, then email) already tolerates a missing one.",
                    "maxLength": 32,
                    "minLength": 1,
                    "type": "string"
                  },
                  "source": {
                    "description": "A short tenant-authored label for where this lead came from. Sanitised and echoed back verbatim as GET /leads's data[].source — never the sourceChannel enum, which this endpoint always sets to \"agent_api\".",
                    "maxLength": 120,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "true when this call inserted a new lead; false when the shared intake's own contact dedup (handle, then phone, then email, per owner and organization) matched an existing one, or an idempotency replay returned the lead a prior call already created.",
                          "type": "boolean"
                        },
                        "leadId": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "leadId",
                        "created"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "An existing lead already matched — the intake's own contact dedup, or an idempotency replay after a claim takeover. `data.created` is false.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "created": {
                          "description": "true when this call inserted a new lead; false when the shared intake's own contact dedup (handle, then phone, then email, per owner and organization) matched an existing one, or an idempotency replay returned the lead a prior call already created.",
                          "type": "boolean"
                        },
                        "leadId": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "leadId",
                        "created"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A new lead was inserted. `data.created` is true.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either an idempotency conflict at the HTTP claim-store layer (see the shared 409 description: idempotency_key_reuse, or request_in_progress with Retry-After), or 409 idempotency_conflict from the lead intake's own idempotency check underneath it — same conflict type, a distinct code and message (\"That idempotency key was already used for a different request.\").",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.write"
            ]
          }
        ],
        "summary": "Create a lead",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.create"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.lead_created"
      }
    },
    "/leads/{id}": {
      "delete": {
        "description": "APPROVAL-GATED: this call deletes nothing. It files a review and answers 202 with a `reviewId`; the lead is deleted only after an OWNER or ADMIN approves the exact action in Settings → Agent access → Reviews, where the card shows the lead as the dashboard does, a count of every kind of attached record that goes with it (activity history, scoring events, nurture enrolments, next actions, follow-up suggestions, research notes, chat-channel links) and what is kept. Bookings, conversations, customers, messages already sent, demo accounts, admissions records, payments and audit history are never deleted. REFUSED AT SUBMISSION, before any review exists: a converted, won or lost lead (409 `lead_converted` / `lead_won` / `lead_lost`), an active demo (409 `active_demo`), admissions records (409 `admissions_linked`), a demo payment or nurture send in flight (409 `demo_conversion_in_flight` / `nurture_send_in_flight`), an ambiguous legacy id (409 `ambiguous_legacy_id`), more than 500 attached records (409 `too_many_attached_rows`), a `duplicateOfLeadId` that is the same lead (409 `duplicate_of_self`) or not this business's (404 `duplicate_not_found`), and a second request while one for the same lead is still waiting (409 `review_already_open`). AT COMMIT the counts are taken again: more attached records than the card showed fails the review as `cascade_grew`, and a lead whose status, stage or last change differs fails it as `target_changed`; a lead already deleted commits as `already_absent` and is not charged. Credits are charged only when an approved deletion runs — never at submission. Retry with the same Idempotency-Key to follow the review: after approval the retry replays the committed receipt.",
        "operationId": "deleteLead",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "duplicateOfLeadId": {
                    "description": "Required when reasonCode is `duplicate`, and refused otherwise: the id of the lead this one duplicates. It must be a different lead of the same business.",
                    "maxLength": 128,
                    "minLength": 1,
                    "type": "string"
                  },
                  "reasonCode": {
                    "description": "Why the lead should go. A closed code, never free text: the owner's approval card translates it.",
                    "enum": [
                      "spam",
                      "duplicate",
                      "test_data"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "reasonCode"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "attachedRecordsDeleted": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "leadId": {
                          "type": "string"
                        },
                        "outcome": {
                          "enum": [
                            "deleted",
                            "already_absent"
                          ],
                          "type": "string"
                        }
                      },
                      "required": [
                        "leadId",
                        "outcome",
                        "attachedRecordsDeleted"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A retry after the owner approved: the committed receipt (`outcome: deleted`, or `already_absent` when the lead was already gone and nothing was charged).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "expiresAt": {
                      "description": "Epoch milliseconds after which an unreviewed request expires.",
                      "type": "integer"
                    },
                    "reviewId": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "pending",
                        "approved"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "reviewId",
                    "status"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Accepted for owner review. Nothing has been deleted; poll by retrying with the same Idempotency-Key.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input (`error.param` names the field): an invalid lead id, a missing or unknown `reasonCode`, `duplicateOfLeadId` missing with `duplicate` or present with any other reason, or equal to the lead's own id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 `lead_not_found` — no lead with that id for this tenant — or 404 `duplicate_not_found` for `duplicateOfLeadId`. Never a 403, so a foreign lead's id cannot be confirmed to exist elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The lead cannot be deleted by an agent (the `error.code` names the refusal, listed in the description), a request for this lead is already waiting for review (`review_already_open`), this request was declined (`review_rejected`) or approved and could not run (`review_failed`), or the Idempotency-Key was reused for a different request (`idempotency_key_reuse`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`review_expired` — the request expired before an owner reviewed it.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.delete"
            ]
          }
        ],
        "summary": "Propose permanently deleting a lead",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.delete"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.delete"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.lead_deleted"
      },
      "get": {
        "description": "Read one lead by its exact id. Same masked projection as `GET /leads`. Refused with 404 lead_not_found for an unresolved id, an ambiguous legacy id, or another tenant's lead — never a 403, so this surface cannot be used to confirm a lead id exists elsewhere.",
        "operationId": "getLead",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "companyName": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "contact": {
                          "additionalProperties": false,
                          "properties": {
                            "nameMasked": {
                              "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                              "type": "string"
                            },
                            "phoneMasked": {
                              "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                              "type": "string"
                            }
                          },
                          "type": "object"
                        },
                        "convertedAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "createdAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "firstTouchAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "id": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "lastActivityAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "qualifiedAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "score": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "source": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "sourceChannel": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "stageId": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "status": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "tags": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "touchCount": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        },
                        "updatedAt": {
                          "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No lead with that id for this tenant. An unresolved id, an ambiguous legacy id and another tenant's lead all answer identically — never a 403 — so this surface cannot be used to confirm a lead id exists elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.read"
            ]
          }
        ],
        "summary": "Get one lead",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.get"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.lead"
      }
    },
    "/leads/{id}/activities": {
      "get": {
        "description": "One page of a lead's activity log, newest first — headers only: `id`, `type` (the activity enum, e.g. `NOTE`, `STAGE_CHANGE`), a bounded and sanitised `title`, and `createdAt`. `description` and `metadata` are never projected, the same free-text rule every read on this surface follows. Answers 200 with an EMPTY page, never a 404, for a lead id that does not resolve, is not this tenant's, or simply has no activity yet — those three are indistinguishable by design: turning the first two into a 404 would need a second round trip and would make this endpoint an existence oracle for `GET /leads/{id}`'s own 404.",
        "operationId": "listLeadActivities",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "createdAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "title": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "type": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.read"
            ]
          }
        ],
        "summary": "List one lead's activity headers",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.activities.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.lead_activities"
      }
    },
    "/leads/{id}/notes": {
      "post": {
        "description": "Appends one NOTE activity to a lead. `description` is the one unbounded-prose field this entire lead surface accepts — write-only: no read capability, at any scope, ever projects `lead_activities.description` back. Writing a note does not touch `lastActivityAt` or `touchCount`, so an agent's note is never mistaken for customer contact by a staleness detector. No scheduler call of any kind: an activity row is inert.",
        "operationId": "addLeadNote",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "description": {
                    "description": "The one unbounded-prose field this entire lead surface accepts. Write-only: no read capability on this API, at any scope, ever projects lead_activities.description back.",
                    "maxLength": 500,
                    "minLength": 1,
                    "type": "string"
                  },
                  "title": {
                    "maxLength": 120,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "title"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "activityId": {
                          "description": "The id of the NOTE activity this call inserted, addressable through GET /leads/{id}/activities.",
                          "type": "string"
                        },
                        "leadId": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "leadId",
                        "activityId"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Created.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input. `error.param` names the offending field. Never billed — refused before admission.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 lead_not_found — no lead with that id for this tenant. Uniform across every lead write: never a 403, so a foreign lead's id cannot be confirmed to exist elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The same Idempotency-Key was already used for a different request body (409 idempotency_key_reuse), or an identical request under this key is still in flight (409 request_in_progress, with Retry-After).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.write"
            ]
          }
        ],
        "summary": "Add a note to a lead",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.note.add"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.lead_note_added"
      }
    },
    "/leads/{id}/stage": {
      "post": {
        "description": "Moves a lead to a non-terminal pipeline stage, reconciling its status and appending a STAGE_CHANGE activity in the same write the dashboard's own transition performs — a mirror of that logic, never the bare four-field patch that skips status reconciliation. TERMINAL STAGES ARE REFUSED, NOT QUEUED: a stage whose isWon/isLost flag (or literal name \"Won\"/\"Lost\"/\"Enrolled\"/\"Signed Up\") marks it won or lost is 400 stage_requires_approval, and so is any move attempted on a lead already in a terminal status — closing or reopening a deal is a revenue fact pending an owner-approved review-queue row, not an unattended write. ONE RESIDUAL COUPLING, stated rather than claimed away: the STAGE_CHANGE activity this call inserts is exactly what the LEAD_STALE_STAGE detector keys off, so a move can make a lead eligible, 10+ days later, for a follow-up PROPOSAL row — a proposal is not a send; the actual send is a separate claim-before-send service. A replay that lands on the lead's current stage and status is a no-op: it returns 200 without appending a second activity.",
        "operationId": "moveLeadStage",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "stageId": {
                    "maxLength": 128,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "stageId"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "leadId": {
                          "type": "string"
                        },
                        "stageId": {
                          "type": "string"
                        },
                        "status": {
                          "description": "The lead's status after the move, reconciled from the target stage's own isWon/isDefault semantics — never the caller's to set directly on this endpoint.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "leadId",
                        "stageId",
                        "status"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Moved (or already on the target stage and status, in which case no second STAGE_CHANGE activity is appended).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either malformed input (`error.param` names the offending field) or 400 stage_requires_approval (`param: stageId`) — the target stage is won or lost, or the lead is already in a terminal state; that transition is set under review rather than by an agent, pending an owner-approved review-queue row.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 lead_not_found (no param) when the lead id does not resolve or belongs to another tenant — checked FIRST, so it is returned whatever stage the request names. 404 stage_not_found (param: stageId) once the lead is confirmed this tenant's and the named stage does not resolve to one of its own pipeline stages; a foreign stage answers identically to a missing one.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The same Idempotency-Key was already used for a different request body (409 idempotency_key_reuse), or an identical request under this key is still in flight (409 request_in_progress, with Retry-After).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.write"
            ]
          }
        ],
        "summary": "Move a lead to a pipeline stage",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.stage.move"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.lead_stage_moved"
      }
    },
    "/leads/{id}/status": {
      "post": {
        "description": "Sets a lead's status to one of NEW, CONTACTED or QUALIFIED — the only three an unattended agent may set. CONVERTED, WON and LOST are revenue facts (`updateLeadStatus` stamps `convertedAt`/`qualifiedAt`, and a terminal state is a number somebody reports) and are refused outright by the request schema itself, since they fall outside the enum this endpoint accepts. A lead already in a terminal state is refused with a SEPARATE 400 status_requires_approval even when the requested value is one of the three writable statuses — editing a terminal lead's status is the same revenue-fact rule from the other direction, pending the same owner-approved review-queue row. No STAGE_CHANGE or other activity row is written by this call, so no follow-up detector keys off a bare status change.",
        "operationId": "updateLeadStatus",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "status": {
                    "description": "The only three statuses an unattended agent may set. CONVERTED, WON and LOST are revenue facts and fall outside this enum — sending one is refused with 400 invalid_request before the request even reaches the lead.",
                    "enum": [
                      "NEW",
                      "CONTACTED",
                      "QUALIFIED"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "status"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "additionalProperties": false,
                      "properties": {
                        "leadId": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "leadId",
                        "status"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Updated.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Either malformed input — status is not one of NEW, CONTACTED, QUALIFIED; a terminal status is refused here as ordinary invalid_request, since it is outside the enum this endpoint accepts — or 400 status_requires_approval (`param: status`) when the requested status is one of the three writable values but the lead's CURRENT status is already terminal.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 lead_not_found — no lead with that id for this tenant. Never a 403, so a foreign lead's id cannot be confirmed to exist elsewhere.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The same Idempotency-Key was already used for a different request body (409 idempotency_key_reuse), or an identical request under this key is still in flight (409 request_in_progress, with Retry-After).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "leads.write"
            ]
          }
        ],
        "summary": "Set a lead's status",
        "tags": [
          "Leads"
        ],
        "x-capability-ids": [
          "leads.status.update"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "leads.write"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.lead_status_updated"
      }
    },
    "/orders": {
      "get": {
        "description": "One page of the tenant's orders with their payment and fulfilment state. Stripe handles (`stripeCheckoutSessionId`, `stripePaymentIntentId`) are deliberately not projected: they are live references to the tenant's payment account, and returning them would make a leaked agent key a foothold on that surface. Amounts are minor units (`totalMinor`) alongside their `currency`. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listOrders",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "createdAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "currency": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "customer": {
                            "additionalProperties": false,
                            "properties": {
                              "nameMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              },
                              "phoneMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "fulfilment": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "paymentStatus": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "placedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "status": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "totalMinor": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "orders.read"
            ]
          }
        ],
        "summary": "List orders",
        "tags": [
          "Orders"
        ],
        "x-capability-ids": [
          "orders.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "orders.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.orders"
      }
    },
    "/payments/links": {
      "post": {
        "description": "Queues ONE Stripe payment link for an unpaid booking, for the business owner to approve under Settings → Agent access → Reviews after a fresh identity check. Nothing is created on this call. The amount is the booking's deposit — or its total when it has none — in QAR, and cannot be chosen; the link is created on the business's OWN Stripe account, never Mawidi's, with no customer email pre-filled. Once approved, the receipt carries `url`. Mawidi sends it to NOBODY: delivering it to the customer is a separate messaging action. If a live link for the booking appeared in the meantime, that link is handed back (`reusedLink: true`) rather than a second one minted. Answers 202 with a `reviewId`. Retry with the SAME Idempotency-Key to follow it: 202 until it has run, then the receipt; a declined, failed or expired request answers 409 review_rejected / review_failed or 410 review_expired, and is closed for good. Daily limits per business (placeholders: 5 refunds and QAR 1,000, 20 payment links, rolling 24 hours) are checked now and again at approval. Only an owner may grant this scope to a key, and it is never part of a scope bundle.",
        "operationId": "createPaymentLink",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "bookingId": {
                    "description": "The booking's id, as GET /bookings or GET /bookings/{id} returned it.",
                    "maxLength": 128,
                    "minLength": 1,
                    "pattern": "^[A-Za-z0-9_-]+$",
                    "type": "string"
                  },
                  "language": {
                    "description": "The payment page's language. Defaults to en.",
                    "enum": [
                      "en",
                      "ar"
                    ],
                    "type": "string"
                  },
                  "reason": {
                    "description": "Optional note for the owner, shown as agent-written text.",
                    "maxLength": 500,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "bookingId"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The link was created or reused (a retry of the same Idempotency-Key after approval replays this receipt).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "expiresAt": {
                      "description": "Epoch milliseconds after which an unreviewed request expires.",
                      "type": "integer"
                    },
                    "reviewId": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "pending",
                        "approved"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "reviewId",
                    "status"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Queued for the owner's approval. Nothing has moved.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input, an undeclared body field, or 400 invalid_request with `param: amountMinor` when the amount is not valid for this payment (more than is left, or not a multiple of 10 for KWD/BHD/OMR). Never queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 target_not_found — no such booking for this tenant. Also returned for a foreign tenant's booking, by design.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "409 target_not_eligible — the booking is cancelled, already paid, already has a live link or an unresolved link request, has nothing payable, or the business has no Stripe account of its own. Also 409 review_already_open (another request for this booking is waiting), 409 review_cap_reached (the daily limit is full), 409 review_rejected / review_failed on a retry, and 409 idempotency_key_reuse.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "410 review_expired — the owner did not decide in time. Nothing moved.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "503 provider_unavailable — the business's Stripe account could not be asked just now; nothing was queued. Or a dependency of the request itself was unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "payments.link"
            ]
          }
        ],
        "summary": "Ask for a payment link for a booking (owner approval required)",
        "tags": [
          "Payments"
        ],
        "x-capability-ids": [
          "payments.links.create"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "payments.link"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.payment_link_created"
      }
    },
    "/payments/refunds": {
      "post": {
        "description": "Queues ONE refund — of the deposit a customer paid for a booking (`bookingId`), or of everything a customer paid for an order (`orderId`), exactly one of the two — for the business owner to approve under Settings → Agent access → Reviews after a fresh identity check. Nothing moves on this call. On approval the refund runs on the business's OWN Stripe account — never Mawidi's — and goes back to the customer's original payment; a refunded order is then marked refunded. For a booking, `amountMinor` is optional: omit it to refund everything still refundable (pinned now, from Stripe); otherwise an integer in the payment's minor units, a multiple of 10 for KWD, BHD and OMR, and no more than what is left. An order is always refunded in full, so omit `amountMinor` for one. `reason` is required and shown to the owner as the agent's own words; it is never sent to Stripe or to the customer. No argument names a currency, card, account or destination, and any other body field is refused with 400. The receipt names what was refunded (`bookingId` or `orderId`, the other null). Answers 202 with a `reviewId`. Retry with the SAME Idempotency-Key to follow it: 202 until it has run, then the receipt; a declined, failed or expired request answers 409 review_rejected / review_failed or 410 review_expired, and is closed for good. Daily limits per business (placeholders: 5 refunds and QAR 1,000, 20 payment links, rolling 24 hours) are checked now and again at approval. Only an owner may grant this scope to a key, and it is never part of a scope bundle.",
        "operationId": "createRefund",
        "parameters": [
          {
            "description": "Caller-chosen, 1-255 characters. Reuse the exact same value to retry a write safely — a replay returns the original response byte-for-byte and is never billed twice. A second body under a key already bound to a different request is refused with 409 idempotency_key_reuse.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "oneOf": [
                  {
                    "required": [
                      "bookingId"
                    ]
                  },
                  {
                    "required": [
                      "orderId"
                    ]
                  }
                ],
                "properties": {
                  "amountMinor": {
                    "description": "Bookings only: an optional partial refund, in the payment's minor units (QAR 10.00 is 1000). Omit for the full remaining amount; always omit it for an order.",
                    "maximum": 99999999,
                    "minimum": 1,
                    "type": "integer"
                  },
                  "bookingId": {
                    "description": "The booking's id, as GET /bookings or GET /bookings/{id} returned it.",
                    "maxLength": 128,
                    "minLength": 1,
                    "pattern": "^[A-Za-z0-9_-]+$",
                    "type": "string"
                  },
                  "orderId": {
                    "description": "The order's id, as GET /orders returned it. The order is refunded in full.",
                    "maxLength": 128,
                    "minLength": 1,
                    "pattern": "^[A-Za-z0-9_-]+$",
                    "type": "string"
                  },
                  "reason": {
                    "description": "Why, for the owner to read. Shown as agent-written text; never sent to Stripe.",
                    "maxLength": 500,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "reason"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The refund ran (a retry of the same Idempotency-Key after approval replays this receipt). `status` is Stripe's refund status.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "expiresAt": {
                      "description": "Epoch milliseconds after which an unreviewed request expires.",
                      "type": "integer"
                    },
                    "reviewId": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "pending",
                        "approved"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "reviewId",
                    "status"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Queued for the owner's approval. Nothing has moved.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed input, an undeclared body field, or 400 invalid_request with `param: amountMinor` when the amount is not valid for this payment (more than is left, or not a multiple of 10 for KWD/BHD/OMR). Never queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization's agent usage quota is exhausted and admission is in `enforce` mode.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "404 target_not_found — no such booking or order for this tenant. Also returned for a foreign tenant's booking or order, by design.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Timed out reading the request body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "409 target_not_eligible — the booking or order was not paid, is fully refunded, its payment is disputed or not settled, is not in QAR, a refund for it is still unresolved, or the business has no Stripe account of its own; an order also when its status can no longer be refunded. Also 409 review_already_open (another request for this booking is waiting), 409 review_cap_reached (the daily limit is full), 409 review_rejected / review_failed on a retry, and 409 idempotency_key_reuse.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "410 review_expired — the owner did not decide in time. Nothing moved.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds the 32 KiB limit.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Content-Type must be application/json.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "503 provider_unavailable — the business's Stripe account could not be asked just now; nothing was queued. Or a dependency of the request itself was unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "payments.refund"
            ]
          }
        ],
        "summary": "Ask to refund a booking's or an order's payment (owner approval required)",
        "tags": [
          "Payments"
        ],
        "x-capability-ids": [
          "payments.refunds.create"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentWrite",
          "limit": 20,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "payments.refund"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "write.payment_refunded"
      }
    },
    "/pipeline/stages": {
      "get": {
        "description": "The tenant's pipeline stages, ordered by `sortOrder` — the same order a dashboard pipeline column renders in. Carries nothing to mask: `lead_pipeline_stages` has no customer column at all. `isWon`/`isLost` are projected because they are exactly what tells an agent which stages `POST /leads/{id}/stage` will refuse with 400 stage_requires_approval. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listPipelineStages",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "isDefault": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "isLost": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "isWon": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "name": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "sortOrder": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "pipeline.read"
            ]
          }
        ],
        "summary": "List pipeline stage configuration",
        "tags": [
          "Pipeline"
        ],
        "x-capability-ids": [
          "pipeline.stages.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "pipeline.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.pipeline_stages"
      }
    },
    "/properties": {
      "get": {
        "description": "One page of the tenant's property listings in both languages. The only row in this family with nothing to mask: `property` carries no customer name, phone or email at all. What is withheld is ACCESS — access notes, key location, viewing hours, street address, unit, building and coordinates — since \"how do I get inside an empty property\" is not a question a listing read needs to answer. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listProperties",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "bathrooms": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "bedrooms": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "builtUpArea": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "city": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "currency": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "district": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "listedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "listingType": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "price": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "propertyType": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "referenceNumber": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "status": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "titleAr": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "titleEn": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "real_estate.read"
            ]
          }
        ],
        "summary": "List property listings",
        "tags": [
          "Real Estate"
        ],
        "x-capability-ids": [
          "real_estate.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "real_estate.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.properties"
      }
    },
    "/service-jobs": {
      "get": {
        "description": "One page of the tenant's field-service jobs with their trade, priority and scheduled window. The service ADDRESS and its coordinates are not projected — a customer's home identifies them more precisely than the phone number this row masks — and neither is the free-text issue description, which routinely repeats both. **Ordering:** rows come back in ascending creation order *within* one organization id form, and the forms follow one another; a tenant whose rows are split across a legacy id alias and its canonical id therefore gets a page that is stable and never skips or repeats a row, but is NOT globally ordered by recency. A cursor walk reads every row exactly once — do not rely on the first page being the newest. To find recent records, walk the pages and compare `createdAt`/`updatedAt` yourself.",
        "operationId": "listServiceJobs",
        "parameters": [
          {
            "description": "Rows per page. Defaults to 50, maximum 100. A non-integer value, or one outside that range, is refused with 400 rather than clamped.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "An opaque cursor from a previous response's `nextCursor`. Omit for the first page and pass it back verbatim; do not parse or construct one. A value this endpoint does not issue — longer than 512 characters, or not of the form `<digits>:<opaque>` — is refused with 400 rather than silently restarting from the first page, so a corrupted cursor cannot make a paging client re-read the tenant from the top without noticing. An empty `cursor=` counts as absent, not malformed. One case is deliberately NOT an error: a well-formed cursor whose leading index names a tenant id form that no longer exists (it was issued when the tenant had more aliases) answers `{ data: [], nextCursor: null, hasMore: false }` — terminal rather than a loop, and the honest answer to what remains under a form that is gone.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "createdAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "customer": {
                            "additionalProperties": false,
                            "properties": {
                              "nameMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              },
                              "phoneMasked": {
                                "description": "Masked by `convex/agent_platform/tenant_mask.ts`; never the stored value. Always a string.",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "id": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "priority": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "scheduledWindowEnd": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "scheduledWindowStart": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "status": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "trade": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          },
                          "updatedAt": {
                            "description": "Declared by the capability's projection; its type is not yet stated in the manifest."
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasMore": {
                      "description": "Exact, not merely `nextCursor !== null`: the reader probes one row beyond a full page and discards it, so `false` means there is genuinely nothing further right now.",
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "description": "Opaque cursor for the next page, or null on the last page. Pass back verbatim as `cursor`.",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Malformed paging input, refused before the query runs. `error.param` names `limit` (not an integer, or outside 1-100) or `cursor` (longer than 512 characters, or not the `<digits>:<opaque>` envelope this endpoint issues). Never billed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "service_jobs.read"
            ]
          }
        ],
        "summary": "List service jobs",
        "tags": [
          "Service Jobs"
        ],
        "x-capability-ids": [
          "service_jobs.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentChangeFeed",
          "limit": 60,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "service_jobs.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.service_jobs"
      }
    },
    "/services": {
      "get": {
        "description": "The organization's active, bookable services in both languages. The agent picks the language for its own conversation.",
        "operationId": "listServices",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "items": {
                        "additionalProperties": false,
                        "properties": {
                          "category": {
                            "maxLength": 120,
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "categoryAr": {
                            "maxLength": 120,
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "durationMinutes": {
                            "minimum": 0,
                            "type": "integer"
                          },
                          "id": {
                            "type": "string"
                          },
                          "nameAr": {
                            "maxLength": 200,
                            "type": "string"
                          },
                          "nameEn": {
                            "maxLength": 200,
                            "type": "string"
                          },
                          "price": {
                            "type": [
                              "number",
                              "null"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "nameEn",
                          "nameAr",
                          "durationMinutes",
                          "price",
                          "category",
                          "categoryAr"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing, malformed, unknown, revoked, or expired API key. The reason is never disclosed in the body — check server logs by `requestId`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The key's scopes do not cover this operation, the agent client is not active, or the organization has not enabled the agent platform.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded — per-key, per-IP, or the org-wide ceiling.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-Usage-Warning": {
                "$ref": "#/components/headers/UsageWarning"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unhandled server error.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dependency this request needs (the rate limiter, the idempotency store, usage metering for a write, or the organization's own record) is temporarily unavailable. Safe to retry shortly.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          }
        },
        "security": [
          {
            "AgentApiKey": [
              "services.read"
            ]
          }
        ],
        "summary": "List bookable services",
        "tags": [
          "Services"
        ],
        "x-capability-ids": [
          "services.list"
        ],
        "x-idempotent": true,
        "x-rate-limit": {
          "bucket": "agentRead",
          "limit": 120,
          "windowSeconds": 60
        },
        "x-required-scopes": [
          "services.read"
        ],
        "x-scope-mode": "all",
        "x-usage-kind": "read.services"
      }
    }
  },
  "security": [
    {
      "AgentApiKey": []
    }
  ],
  "servers": [
    {
      "description": "Production",
      "url": "https://mawidi.com/api/agent/v1"
    },
    {
      "description": "Local development",
      "url": "http://localhost:9000/api/agent/v1"
    }
  ],
  "tags": [
    {
      "description": "The tenant's bookable service catalogue.",
      "name": "Services"
    },
    {
      "description": "Open slot computation, sharing the exact grid the write path enforces.",
      "name": "Availability"
    },
    {
      "description": "Create, read, list, reschedule, cancel, and poll changes to bookings.",
      "name": "Bookings"
    },
    {
      "description": "Enumeration-safe find-or-create by phone number, and the masked customer book.",
      "name": "Customers"
    },
    {
      "description": "Order state and payment/fulfilment status. Never the tenant's Stripe handles.",
      "name": "Orders"
    },
    {
      "description": "Field-service jobs, their trade and scheduled window. Never the service address.",
      "name": "Service Jobs"
    },
    {
      "description": "Property listings in both languages. Never access notes or coordinates.",
      "name": "Real Estate"
    },
    {
      "description": "Conversation thread headers. Never message bodies.",
      "name": "Conversations"
    },
    {
      "description": "The tenant's CRM leads and their activity log. Masked contact, no free text, no email.",
      "name": "Leads"
    },
    {
      "description": "Pipeline stage configuration — no customer data to mask.",
      "name": "Pipeline"
    },
    {
      "description": "Tenant configuration: service catalogue management, business hours, organization settings, the WhatsApp AI knowledge base, and a derived voice-agent summary. Every credential, billing, auth and retention field is withheld; no delete.",
      "name": "Config"
    },
    {
      "description": "This API client's own per-capability usage receipts, for reconciliation and audit export. Never another client's rows, never organization-wide. No SLA/uptime reporting.",
      "name": "Audit"
    },
    {
      "description": "Replies to customers, proposed by the agent and sent only after the business owner approves each one. WhatsApp only, inside the customer's 24-hour window, never to a customer who opted out.",
      "name": "Messages"
    },
    {
      "description": "Refunds and payment links for a booking, on the business's own Stripe account. Every call waits for the owner's approval after an identity check; nothing moves until then.",
      "name": "Payments"
    }
  ]
}
