{
  "openapi": "3.1.0",
  "info": {
    "title": "Customer Alliance API",
    "summary": "Review management, surveys & feedback for hospitality — as an API.",
    "version": "4.0.0",
    "description": "The Customer Alliance API gives you programmatic access to your reviews, surveys, analytics and AI insights, and lets you subscribe to events via outbound webhooks.\n\n## Getting started\n\n1. In the dashboard, go to **Settings → API** and create a key, granting only the scopes you need.\n2. Copy the key (`ca_live_…`) — it's shown once.\n3. Send it as a Bearer token on every request:\n\n```bash\ncurl https://app.customer-alliance.com/api/v4/reviews \\\n  -H \"Authorization: Bearer ca_live_your_key_here\"\n```\n\nCall `GET /whoami` to confirm the key resolves to the account, node and scopes you expect.\n\n## Authentication\n\nEvery request needs `Authorization: Bearer <key>`. Keys are created and scoped in **Settings → API**.\n\n## Scopes\n\nEach key holds a subset of `reviews:read`, `surveys:read`, `analytics:read`, `insights:read`, `nodes:read`, `webhooks:manage`, `guests:write`. A call missing its scope returns `403`.\n\n## Hierarchy\n\nA key is bound to one node in your account hierarchy; **read** endpoints cover that node and its subtree. The webhook-management endpoints (`/webhooks*`) are the exception — they operate account-wide and ignore the key's node binding (see **Webhooks**).\n\n## Pagination\n\nList endpoints are cursor-paginated: pass the previous response's `next_cursor` as `?cursor=`, up to `?limit=` (max 100).\n\n## Rate limiting\n\nRequests are rate-limited per key (fixed window). A `429` response includes a `Retry-After` header — honour it before retrying.\n\n## Webhooks\n\nRegister endpoints to receive signed event deliveries (see **Webhook events**). Verify the `X-CA-Signature` HMAC on every delivery before trusting it.\n\nWebhook endpoints and event delivery are **account-scoped**: a `webhooks:manage` key manages every endpoint in the account and its endpoints receive events from the whole account, regardless of the node the key is bound to. Node binding applies to read endpoints only.",
    "termsOfService": "https://www.customer-alliance.com/en/terms-and-conditions/",
    "contact": {
      "name": "Customer Alliance Support",
      "email": "info@customer-alliance.com",
      "url": "https://www.customer-alliance.com/"
    }
  },
  "externalDocs": {
    "description": "Customer Alliance",
    "url": "https://www.customer-alliance.com/"
  },
  "servers": [
    {
      "url": "https://app.customer-alliance.com/api/v4",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Identity",
      "description": "Who the key is."
    },
    {
      "name": "Reviews",
      "description": "Collected reviews and owner replies."
    },
    {
      "name": "Analytics",
      "description": "Score and rating aggregates."
    },
    {
      "name": "Nodes",
      "description": "Hierarchy discovery."
    },
    {
      "name": "Surveys",
      "description": "Surveys, responses and answers (PII-free)."
    },
    {
      "name": "Insights",
      "description": "AI recommendations, mentions and topics."
    },
    {
      "name": "Guests",
      "description": "Push guests into a property (write-only; grants no read of guest data)."
    },
    {
      "name": "Webhooks",
      "description": "Manage outbound webhook endpoints and inspect deliveries. Account-scoped: these endpoints ignore the key's node binding (unlike the read endpoints)."
    },
    {
      "name": "Webhook events",
      "description": "The signed events Customer Alliance POSTs to your endpoints."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key created in Settings → API (`ca_live_…`)."
      }
    },
    "parameters": {
      "cursor": {
        "name": "cursor",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Opaque pagination cursor from a prior response's next_cursor."
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key lacks the required scope, or the account's plan does not include it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found or outside the key's scope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Honour the `Retry-After` header.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "example": {
          "error": "Invalid API key"
        }
      },
      "Review": {
        "type": "object",
        "description": "A collected review. Hidden reviews are included and flagged `is_hidden`; the owner's own reply text is `owner_reply`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string",
            "example": "google"
          },
          "external_id": {
            "type": "string",
            "description": "The review's id on the source platform."
          },
          "author": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null when the review is anonymised."
          },
          "rating": {
            "type": [
              "number",
              "null"
            ],
            "description": "Rating on a 0–5 scale."
          },
          "rating_normalized": {
            "type": [
              "number",
              "null"
            ],
            "description": "Rating normalised to 0–100."
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "sentiment": {
            "type": [
              "string",
              "null"
            ],
            "description": "positive | neutral | negative | null."
          },
          "owner_reply": {
            "type": [
              "string",
              "null"
            ]
          },
          "replied_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reviewed_at": {
            "type": "string",
            "format": "date-time"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "is_hidden": {
            "type": "boolean"
          }
        }
      },
      "AnalyticsScores": {
        "type": "object",
        "description": "Aggregate reputation scores for the key's node subtree.",
        "properties": {
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "overall_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "Overall score on a 0–5 scale (one decimal)."
          },
          "overall_score_normalized": {
            "type": [
              "number",
              "null"
            ],
            "description": "Overall score normalised to 0–100 (may carry one decimal; round for whole-percent display)."
          },
          "total_reviews": {
            "type": "integer"
          },
          "recommend_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "response_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "platform_scores": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "platform": {
                  "type": "string"
                },
                "score": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Platform score on a 0–5 scale (one decimal)."
                },
                "score_normalized": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Platform score normalised to 0–100 (may carry one decimal; round for whole-percent display)."
                },
                "reviews": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "Node": {
        "type": "object",
        "description": "A hierarchy node (business unit / property) within the key's scope.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "example": "property"
          }
        }
      },
      "Survey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "example": "published"
          },
          "primary_language": {
            "type": "string",
            "example": "en"
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SurveyDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Survey"
          },
          {
            "type": "object",
            "properties": {
              "published_definition": {
                "type": "object",
                "description": "The published survey definition (pages/questions)."
              }
            }
          }
        ]
      },
      "SurveyResponse": {
        "type": "object",
        "description": "A survey response. Respondent PII (email, personalisation context, tokens) is never exposed.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "survey_id": {
            "type": "string",
            "format": "uuid"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "example": "completed"
          },
          "channel": {
            "type": "string",
            "example": "email"
          },
          "overall_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SurveyAnswer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "question_id": {
            "type": "string"
          },
          "question_path": {
            "type": "string"
          },
          "question_type": {
            "type": "string",
            "example": "rating"
          },
          "value": {
            "description": "The answer value; shape depends on question_type."
          },
          "numeric_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "comment": {
            "type": [
              "string",
              "null"
            ]
          },
          "answered_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Recommendation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "priority": {
            "type": "string",
            "example": "high"
          },
          "impact": {
            "type": [
              "string",
              "null"
            ]
          },
          "canonical_label_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Mention": {
        "type": "object",
        "description": "A topic mention extracted from a review. `is_hidden` mirrors the source review's flag.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "review_id": {
            "type": "string",
            "format": "uuid"
          },
          "source_field": {
            "type": "string",
            "example": "text"
          },
          "text": {
            "type": "string"
          },
          "sentiment": {
            "type": [
              "string",
              "null"
            ]
          },
          "canonical_label_id": {
            "type": "string",
            "format": "uuid"
          },
          "custom_category_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ]
          },
          "is_hidden": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Topic": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "parent_label_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "WhoAmI": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "example": {
          "account_id": "11111111-1111-1111-1111-111111111111",
          "node_id": "42222222-2222-2222-2222-222222222222",
          "scopes": [
            "reviews:read",
            "webhooks:manage"
          ]
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "description": "A registered outbound webhook endpoint. The signing secret is never returned after creation.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "review.received",
                "review.replied",
                "review.alert_triggered",
                "survey.response_completed",
                "guest.created",
                "guest.unsubscribed"
              ]
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointInput": {
        "type": "object",
        "required": [
          "name",
          "url",
          "events"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Delivery URL. Must be http(s); private/reserved addresses are rejected at delivery time."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "review.received",
                "review.replied",
                "review.alert_triggered",
                "survey.response_completed",
                "guest.created",
                "guest.unsubscribed"
              ]
            }
          },
          "is_active": {
            "type": "boolean",
            "description": "PATCH only — pause/resume deliveries without deleting the endpoint."
          }
        },
        "example": {
          "name": "Prod integration",
          "url": "https://example.com/hooks/ca",
          "events": [
            "review.received",
            "survey.response_completed"
          ]
        }
      },
      "WebhookEndpointCreated": {
        "type": "object",
        "required": [
          "secret",
          "endpoint"
        ],
        "properties": {
          "secret": {
            "type": "string",
            "description": "The signing secret (`whsec_…`). Returned exactly once — store it now; you cannot retrieve it again.",
            "example": "whsec_XXXXXXXXXXXXXXXXXXXXXXXX"
          },
          "endpoint": {
            "$ref": "#/components/schemas/WebhookEndpoint"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event_type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempt_count": {
            "type": "integer"
          },
          "last_status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ReservationInput": {
        "type": "object",
        "required": [
          "external_id"
        ],
        "description": "A reservation (stay) to create or update for the guest, mirroring what a PMS integration syncs. Identity is (property, external_id): a repeat push of the same external_id updates the stay in place (e.g. a status change from booked to checked_out) rather than creating a duplicate. Pushing stays lets you build audiences on stay status, booking channel, and recently-ended stays.",
        "properties": {
          "external_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Required — your booking reference; the stay's dedupe key."
          },
          "status": {
            "type": "string",
            "enum": [
              "booked",
              "arrived",
              "checked_out",
              "cancelled",
              "no_show"
            ],
            "description": "Canonical stay status. Defaults to `booked` when omitted."
          },
          "raw_status": {
            "type": "string",
            "maxLength": 120,
            "description": "Your own status label, kept verbatim for display."
          },
          "check_in": {
            "type": "string",
            "format": "date-time",
            "description": "Stay start. ISO-8601 date or date-time."
          },
          "check_out": {
            "type": "string",
            "format": "date-time",
            "description": "Stay end. ISO-8601 date or date-time — drives the recently-ended-stay audience filter."
          },
          "checked_in_at": {
            "type": "string",
            "format": "date-time"
          },
          "checked_out_at": {
            "type": "string",
            "format": "date-time"
          },
          "channel": {
            "type": "string",
            "maxLength": 120,
            "description": "Booking origin, e.g. booking.com."
          },
          "custom_fields": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "number",
                "boolean"
              ]
            },
            "description": "Merged into the stay's custom fields; omitted keys are preserved."
          }
        },
        "example": {
          "external_id": "BK-1001",
          "status": "checked_out",
          "check_in": "2026-08-01",
          "check_out": "2026-08-04",
          "channel": "booking.com"
        }
      },
      "GuestPushInput": {
        "type": "object",
        "required": [
          "email"
        ],
        "description": "A guest to create or update at one property. Identity is (property, email): a repeat push of the same email to the same property updates that guest rather than creating a duplicate. Optionally include a `reservation` to upsert the guest's stay in the same call.",
        "properties": {
          "property_id": {
            "type": "string",
            "format": "uuid",
            "description": "The property (leaf node) to attach the guest to. Optional when the key is bound to a single property; required when the key is bound to a group. Must be a property within the key's node scope."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 320,
            "description": "Required — normalized (trimmed + lower-cased) and used as the dedupe key."
          },
          "salutation": {
            "type": "string",
            "maxLength": 50
          },
          "first_name": {
            "type": "string",
            "maxLength": 200
          },
          "last_name": {
            "type": "string",
            "maxLength": 200
          },
          "phone": {
            "type": "string",
            "maxLength": 40
          },
          "country_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 (e.g. DE)."
          },
          "language_code": {
            "type": "string",
            "maxLength": 5,
            "description": "e.g. de or de-DE."
          },
          "custom_fields": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "number",
                "boolean"
              ]
            },
            "description": "Merged into the guest's custom fields; omitted keys are preserved."
          },
          "marketing_consent": {
            "type": "boolean",
            "description": "When set, records invitation-email consent. An explicit prior withdrawal is never overridden by a grant (GDPR)."
          },
          "reservation": {
            "$ref": "#/components/schemas/ReservationInput"
          }
        },
        "example": {
          "property_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "email": "jane.doe@example.com",
          "first_name": "Jane",
          "last_name": "Doe",
          "marketing_consent": true,
          "reservation": {
            "external_id": "BK-1001",
            "status": "checked_out",
            "check_in": "2026-08-01",
            "check_out": "2026-08-04",
            "channel": "booking.com"
          }
        }
      },
      "Reservation": {
        "type": "object",
        "description": "A reservation (stay) as stored. Returned inside a `Guest` when the push included a `reservation`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "booked",
              "arrived",
              "checked_out",
              "cancelled",
              "no_show"
            ]
          },
          "raw_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "check_in": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "check_out": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checked_in_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checked_out_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "channel": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Guest": {
        "type": "object",
        "description": "A guest record as stored. `source` is `api` for guests created through this endpoint.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "property_id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "type": "string",
            "example": "api"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "salutation": {
            "type": [
              "string",
              "null"
            ]
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "language_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "reservation": {
            "$ref": "#/components/schemas/Reservation",
            "description": "Present only when the push included a reservation."
          }
        }
      },
      "WebhookEnvelope": {
        "type": "object",
        "description": "Every delivery POSTs this envelope. Verify the `X-CA-Signature: sha256=<hex>` header — an HMAC-SHA256 of the raw request body keyed by your endpoint's signing secret — before trusting it.",
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Delivery id (also in the X-CA-Delivery-Id header); stable across retries — use it to dedupe."
          },
          "event": {
            "type": "string",
            "enum": [
              "review.received",
              "review.replied",
              "review.alert_triggered",
              "survey.response_completed",
              "guest.created",
              "guest.unsubscribed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "description": "The event payload; shape depends on `event` (see the schemas below)."
          }
        }
      },
      "ReviewReceivedData": {
        "type": "object",
        "properties": {
          "review_id": {
            "type": "string",
            "format": "uuid"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string"
          }
        }
      },
      "ReviewRepliedData": {
        "type": "object",
        "properties": {
          "review_id": {
            "type": "string",
            "format": "uuid"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "reply_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ReviewAlertTriggeredData": {
        "type": "object",
        "description": "Note: this payload uses camelCase keys (historical).",
        "properties": {
          "alertId": {
            "type": "string",
            "format": "uuid"
          },
          "ruleId": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "context": {
            "type": "object"
          }
        }
      },
      "SurveyResponseCompletedData": {
        "type": "object",
        "properties": {
          "survey_id": {
            "type": "string",
            "format": "uuid"
          },
          "response_id": {
            "type": "string",
            "format": "uuid"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "overall_score": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "GuestCreatedData": {
        "type": "object",
        "properties": {
          "guest_id": {
            "type": "string",
            "format": "uuid"
          },
          "node_id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "type": "string",
            "example": "import"
          }
        }
      },
      "GuestUnsubscribedData": {
        "type": "object",
        "properties": {
          "guest_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "unsubscribe_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      }
    }
  },
  "paths": {
    "/whoami": {
      "get": {
        "summary": "The authenticated key's account, node and scopes",
        "operationId": "whoami",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoAmI"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/reviews": {
      "get": {
        "summary": "List reviews for the key's node subtree (newest first)",
        "description": "Requires `reviews:read`. Hidden reviews are included and marked `is_hidden=true` — filter on that flag to exclude them (unlike the public widget/certificate surfaces, which omit hidden reviews entirely).",
        "operationId": "listReviews",
        "tags": [
          "Reviews"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of reviews.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor",
                    "has_more"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Review"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `?cursor=` to fetch the next page; null on the last page."
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/reviews/{id}": {
      "get": {
        "summary": "Get a single review by id",
        "description": "Requires `reviews:read`. Returns hidden reviews too (marked `is_hidden=true`).",
        "operationId": "getReview",
        "tags": [
          "Reviews"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Review"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/analytics/scores": {
      "get": {
        "summary": "Aggregate reputation scores for the key's node subtree",
        "description": "Requires `analytics:read`.",
        "operationId": "getAnalyticsScores",
        "tags": [
          "Analytics"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsScores"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/nodes": {
      "get": {
        "summary": "List hierarchy nodes within the key's scope",
        "description": "Requires `nodes:read`.",
        "operationId": "listNodes",
        "tags": [
          "Nodes"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Node"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/surveys": {
      "get": {
        "summary": "List published surveys whose owner node is in the key's subtree",
        "description": "Requires `surveys:read`.",
        "operationId": "listSurveys",
        "tags": [
          "Surveys"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of surveys.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor",
                    "has_more"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Survey"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `?cursor=` to fetch the next page; null on the last page."
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/surveys/{id}": {
      "get": {
        "summary": "Get a single published survey (with its published definition)",
        "description": "Requires `surveys:read`.",
        "operationId": "getSurvey",
        "tags": [
          "Surveys"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SurveyDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/surveys/{id}/responses": {
      "get": {
        "summary": "List responses for a survey (newest first)",
        "description": "Requires `surveys:read`. Respondent PII is not included.",
        "operationId": "listSurveyResponses",
        "tags": [
          "Surveys"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of responses.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor",
                    "has_more"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SurveyResponse"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `?cursor=` to fetch the next page; null on the last page."
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/surveys/{id}/responses/{responseId}/answers": {
      "get": {
        "summary": "List the answers for a single response",
        "description": "Requires `surveys:read`.",
        "operationId": "listResponseAnswers",
        "tags": [
          "Surveys"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "responseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SurveyAnswer"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/insights/recommendations": {
      "get": {
        "summary": "List AI recommendations for the account (newest first)",
        "description": "Requires `insights:read`.",
        "operationId": "listRecommendations",
        "tags": [
          "Insights"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of recommendations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor",
                    "has_more"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Recommendation"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `?cursor=` to fetch the next page; null on the last page."
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/insights/mentions": {
      "get": {
        "summary": "List topic mentions extracted from reviews in the key's subtree (newest first)",
        "description": "Requires `insights:read`.",
        "operationId": "listMentions",
        "tags": [
          "Insights"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of mentions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor",
                    "has_more"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Mention"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `?cursor=` to fetch the next page; null on the last page."
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/insights/topics": {
      "get": {
        "summary": "List the canonical topic labels readable by the account",
        "description": "Requires `insights:read`.",
        "operationId": "listTopics",
        "tags": [
          "Insights"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Topic"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/guests": {
      "post": {
        "summary": "Create or update a guest at a property",
        "description": "Requires `guests:write`. Push a guest into the Guests section — for a CRM/PMS/booking integration to feed guests in. Write-only: this scope grants no read of guest data. Identity is (property, email), so a repeat push of the same email to the same property updates that guest (`200`) rather than creating a duplicate (`201` on first create). Include an optional `reservation` to upsert the guest's stay in the same call (identity (property, reservation.external_id)), so you can build audiences on stay status, booking channel, and recently-ended stays. A guest erased under GDPR is never repopulated (`409`).",
        "operationId": "pushGuest",
        "tags": [
          "Guests"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GuestPushInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing guest updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Guest"
                }
              }
            }
          },
          "201": {
            "description": "Guest created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Guest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The guest has been erased (GDPR) and can no longer be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "property_id is missing, out of scope, or not a property (leaf) node.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "summary": "List the account's outbound webhook endpoints",
        "description": "Requires `webhooks:manage`. Signing secrets are never returned.",
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "summary": "Register an outbound webhook endpoint",
        "description": "Requires `webhooks:manage`. The signing secret is returned exactly once — store it now.",
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created — secret shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Per-account endpoint limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}": {
      "patch": {
        "summary": "Update a webhook endpoint",
        "description": "Requires `webhooks:manage`. Any subset of name / url / events / is_active.",
        "operationId": "updateWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook endpoint (cascades its deliveries)",
        "description": "Requires `webhooks:manage`.",
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "summary": "Recent delivery attempts for one endpoint (newest first)",
        "description": "Requires `webhooks:manage`.",
        "operationId": "listWebhookDeliveries",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "summary": "Send a test `ping` delivery to one endpoint",
        "description": "Requires `webhooks:manage`. Queues a signed ping regardless of the endpoint's subscribed events.",
        "operationId": "testWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK — ping queued."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "webhooks": {
    "review.received": {
      "post": {
        "summary": "`review.received` delivered to your endpoint",
        "description": "Your endpoint receives this POST. Respond 2xx to acknowledge; non-2xx (and 3xx) are retried with backoff.",
        "operationId": "webhook_review_received",
        "tags": [
          "Webhook events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "event": {
                        "const": "review.received"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ReviewReceivedData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "review.replied": {
      "post": {
        "summary": "`review.replied` delivered to your endpoint",
        "description": "Your endpoint receives this POST. Respond 2xx to acknowledge; non-2xx (and 3xx) are retried with backoff.",
        "operationId": "webhook_review_replied",
        "tags": [
          "Webhook events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "event": {
                        "const": "review.replied"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ReviewRepliedData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "review.alert_triggered": {
      "post": {
        "summary": "`review.alert_triggered` delivered to your endpoint",
        "description": "Your endpoint receives this POST. Respond 2xx to acknowledge; non-2xx (and 3xx) are retried with backoff.",
        "operationId": "webhook_review_alert_triggered",
        "tags": [
          "Webhook events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "event": {
                        "const": "review.alert_triggered"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ReviewAlertTriggeredData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "survey.response_completed": {
      "post": {
        "summary": "`survey.response_completed` delivered to your endpoint",
        "description": "Your endpoint receives this POST. Respond 2xx to acknowledge; non-2xx (and 3xx) are retried with backoff.",
        "operationId": "webhook_survey_response_completed",
        "tags": [
          "Webhook events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "event": {
                        "const": "survey.response_completed"
                      },
                      "data": {
                        "$ref": "#/components/schemas/SurveyResponseCompletedData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "guest.created": {
      "post": {
        "summary": "`guest.created` delivered to your endpoint",
        "description": "Your endpoint receives this POST. Respond 2xx to acknowledge; non-2xx (and 3xx) are retried with backoff.",
        "operationId": "webhook_guest_created",
        "tags": [
          "Webhook events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "event": {
                        "const": "guest.created"
                      },
                      "data": {
                        "$ref": "#/components/schemas/GuestCreatedData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "guest.unsubscribed": {
      "post": {
        "summary": "`guest.unsubscribed` delivered to your endpoint",
        "description": "Your endpoint receives this POST. Respond 2xx to acknowledge; non-2xx (and 3xx) are retried with backoff.",
        "operationId": "webhook_guest_unsubscribed",
        "tags": [
          "Webhook events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "event": {
                        "const": "guest.unsubscribed"
                      },
                      "data": {
                        "$ref": "#/components/schemas/GuestUnsubscribedData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    }
  }
}
