{
  "openapi": "3.0.3",
  "info": {
    "title": "FSRevs Integration API",
    "version": "1.0.0-beta.4",
    "description": "Beta. Security, tenancy, idempotency, and delivery guarantees are production quality; the contract may evolve before Stable. Customers provide attribution and data, never implicit delivery instructions."
  },
  "servers": [{"url": "https://{tenant}.fsrevs.com/api/v1", "variables": {"tenant": {"default": "example", "description": "Existing FSRevs tenant subdomain."}}}],
  "security": [{"bearerAuth": []}],
  "x-webhook-protocol": {
    "description": "Outbound webhook requests use the exact raw JSON body serialized with JSON_UNESCAPED_SLASHES. The lowercase hexadecimal HMAC-SHA256 input is decimal_unix_timestamp + '.' + raw_body and the X-FSRevs-Signature value is prefixed with v1=. The event ID is not a separate HMAC component; it is present in the signed body and repeated in X-FSRevs-Event-Id. X-FSRevs-Subscription-Id is unsigned lookup metadata for authenticated adapters that cannot access the exact raw request bytes; it does not alter the signed payload or HMAC input.",
    "headers": ["Content-Type", "User-Agent", "X-FSRevs-Subscription-Id", "X-FSRevs-Event-Id", "X-FSRevs-Timestamp", "X-FSRevs-Signature"],
    "user_agent": "FSRevs-Webhooks/1.0-beta",
    "delivery": "At least once. The initial attempt is followed by up to seven retries after 1m, 5m, 15m, 1h, 3h, 6h, and 12h. Manual redelivery is one attempt. Event ID and raw JSON body remain stable; timestamp and signature headers are regenerated per attempt."
  },
  "paths": {
    "/connection": {"get": {
      "operationId": "getConnection", "x-route-name": "tenant.api.v1.connection.show", "x-beta": true, "x-required-abilities": [], "summary": "Validate a tenant API connection",
      "description": "Returns the authenticated workspace identity needed by integration adapters. A valid current tenant API credential is sufficient; no unrelated resource or webhook ability is required.",
      "responses": {"200": {"$ref": "#/components/responses/ConnectionOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/customers": {"post": {
      "operationId": "createCustomer", "x-route-name": "tenant.api.v1.customers.store", "x-beta": true, "x-required-ability": "customers:write", "summary": "Create a Customer",
      "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateCustomer"}}}},
      "responses": {"201": {"$ref": "#/components/responses/CustomerCreated"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/customers/synchronize": {"post": {
      "operationId": "synchronizeCustomer", "x-route-name": "tenant.api.v1.customers.synchronize", "x-beta": true, "x-required-abilities": ["customers:read", "customers:write"], "summary": "Create or update a Customer",
      "description": "Atomically matches, creates, or updates one Customer. Supply source + external_id together for durable synchronization and optional source_updated_at protection, or omit all three and supply an exact email or phone for unlinked one-off intake. This operation never creates Review Requests, Custom Form requests, message intents, email, or SMS.",
      "parameters": [{"$ref": "#/components/parameters/UuidIdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SynchronizeCustomer"}}}},
      "responses": {"200": {"$ref": "#/components/responses/CustomerSynchronized"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/customers/lookup": {"post": {
      "operationId": "lookupCustomer", "x-route-name": "tenant.api.v1.customers.lookup", "x-beta": true, "x-required-ability": "customers:read", "summary": "Look up a Customer by deterministic identity",
      "description": "Read-only body-based lookup. Identity values are never accepted in URL paths or query strings. Name is never a deterministic identity.",
      "requestBody": {"required": true, "content": {"application/json": {
        "schema": {"oneOf": [{"$ref": "#/components/schemas/ContactIdentityLookup"}, {"$ref": "#/components/schemas/ExternalReferenceLookup"}], "discriminator": {"propertyName": "type"}},
        "examples": {"contact": {"value": {"type": "contact_identity", "email": "john@example.com", "phone": "+18085551212"}}, "external": {"value": {"type": "external_reference", "source": "servicetitan-production", "external_id": "Customer-ABC123"}}}
      }}},
      "responses": {"200": {"$ref": "#/components/responses/CustomerOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/customers/{customer_id}": {
      "parameters": [{"$ref": "#/components/parameters/CustomerId"}],
      "get": {"operationId": "getCustomer", "x-route-name": "tenant.api.v1.customers.show", "x-beta": true, "x-required-ability": "customers:read", "responses": {"200": {"$ref": "#/components/responses/CustomerOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}},
      "patch": {"operationId": "updateCustomer", "x-route-name": "tenant.api.v1.customers.update", "x-beta": true, "x-required-ability": "customers:write", "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateCustomer"}}}}, "responses": {"200": {"$ref": "#/components/responses/CustomerUpdated"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}
    },
    "/customers/{customer_id}/external-references": {
      "parameters": [{"$ref": "#/components/parameters/CustomerId"}],
      "get": {"operationId": "listCustomerExternalReferences", "x-route-name": "tenant.api.v1.customers.external-references.index", "x-beta": true, "x-required-ability": "customers:read", "responses": {"200": {"$ref": "#/components/responses/ExternalReferenceList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}},
      "post": {"operationId": "createCustomerExternalReference", "x-route-name": "tenant.api.v1.customers.external-references.store", "x-beta": true, "x-required-ability": "customers:write", "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExternalReferenceWrite"}}}}, "responses": {"201": {"$ref": "#/components/responses/ExternalReferenceCreated"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}
    },
    "/customers/{customer_id}/external-references/{external_reference_id}": {
      "parameters": [{"$ref": "#/components/parameters/CustomerId"}, {"$ref": "#/components/parameters/ExternalReferenceId"}],
      "delete": {"operationId": "deleteCustomerExternalReference", "x-route-name": "tenant.api.v1.customers.external-references.destroy", "x-beta": true, "x-required-abilities": ["customers:read", "customers:write"], "summary": "Unlink one Customer external reference", "description": "Deletes only the explicitly identified external-reference link and preserves the Customer. Repeating the same deletion is safe. Disable the calling automation before unlinking and ensure a later synchronization can uniquely match email or phone before expecting relinking. Bulk clearing is intentionally unavailable.", "responses": {"204": {"description": "The reference is unlinked or was already absent from this Customer."}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}
    },
    "/review-requests": {"post": {
      "operationId": "sendReviewRequest", "x-route-name": "tenant.api.v1.review-requests.store", "x-beta": true, "x-required-ability": "review_requests:send",
      "description": "Sends only to explicitly supplied destinations. Email and SMS channels are derived from the supplied email and phone; stored Customer contacts are never substituted.",
      "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SendReviewRequest"}}}},
      "responses": {"201": {"$ref": "#/components/responses/ReviewRequestCreated"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/review-requests/{review_request_id}": {"get": {
      "operationId": "getReviewRequest", "x-route-name": "tenant.api.v1.review-requests.show", "x-beta": true, "x-required-ability": "review_requests:read", "parameters": [{"$ref": "#/components/parameters/ReviewRequestId"}],
      "responses": {"200": {"$ref": "#/components/responses/ReviewRequestOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/custom-form-requests": {"post": {
      "operationId": "sendCustomFormRequest", "x-route-name": "tenant.api.v1.custom-form-requests.store", "x-beta": true, "x-required-ability": "custom_form_requests:send",
      "description": "Sends only to explicitly supplied destinations. Email and SMS channels are derived from the supplied email and phone; stored Customer contacts are never substituted.",
      "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SendCustomFormRequest"}}}},
      "responses": {"201": {"$ref": "#/components/responses/CustomFormRequestCreated"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/custom-form-requests/{custom_form_request_id}": {"get": {
      "operationId": "getCustomFormRequest", "x-route-name": "tenant.api.v1.custom-form-requests.show", "x-beta": true, "x-required-ability": "custom_form_requests:read", "description": "Returns authorized dynamic answer data. Attachments are excluded.", "parameters": [{"$ref": "#/components/parameters/CustomFormRequestId"}],
      "responses": {"200": {"$ref": "#/components/responses/CustomFormRequestOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/locations": {"get": {"operationId": "listLocations", "x-route-name": "tenant.api.v1.locations.index", "x-beta": true, "x-required-ability": "configuration:read", "parameters": [{"$ref": "#/components/parameters/ResourceSearch"}, {"$ref": "#/components/parameters/ResourceSearchLimit"}], "responses": {"200": {"$ref": "#/components/responses/LocationList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}},
    "/locations/{location_id}/review-flows": {"get": {"operationId": "listReviewFlows", "x-route-name": "tenant.api.v1.locations.review-flows.index", "x-beta": true, "x-required-ability": "configuration:read", "parameters": [{"$ref": "#/components/parameters/LocationId"}, {"$ref": "#/components/parameters/ResourceSearch"}, {"$ref": "#/components/parameters/ResourceSearchLimit"}], "responses": {"200": {"$ref": "#/components/responses/ReviewFlowList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}},
    "/custom-forms": {"get": {"operationId": "listCustomForms", "x-route-name": "tenant.api.v1.custom-forms.index", "x-beta": true, "x-required-ability": "configuration:read", "parameters": [{"$ref": "#/components/parameters/OptionalLocationId"}, {"$ref": "#/components/parameters/ResourceSearch"}, {"$ref": "#/components/parameters/ResourceSearchLimit"}], "responses": {"200": {"$ref": "#/components/responses/CustomFormList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}},
    "/review-flows/{review_flow_id}/employees": {"get": {"operationId": "listReviewFlowEmployees", "x-route-name": "tenant.api.v1.review-flows.employees.index", "x-beta": true, "x-required-ability": "configuration:read", "parameters": [{"$ref": "#/components/parameters/ReviewFlowId"}, {"$ref": "#/components/parameters/RequiredLocationIdQuery"}], "responses": {"200": {"$ref": "#/components/responses/UserOptionList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}},
    "/custom-forms/{custom_form_id}/employees": {"get": {"operationId": "listCustomFormEmployees", "x-route-name": "tenant.api.v1.custom-forms.employees.index", "x-beta": true, "x-required-ability": "configuration:read", "parameters": [{"$ref": "#/components/parameters/CustomFormId"}, {"$ref": "#/components/parameters/OptionalLocationId"}], "responses": {"200": {"$ref": "#/components/responses/UserOptionList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}},
    "/proxy-senders": {"get": {"operationId": "listProxySenders", "x-route-name": "tenant.api.v1.proxy-senders.index", "x-beta": true, "x-required-ability": "configuration:read", "parameters": [{"$ref": "#/components/parameters/OptionalLocationId"}], "responses": {"200": {"$ref": "#/components/responses/UserOptionList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}},
    "/webhook-subscriptions": {
      "get": {"operationId": "listWebhookSubscriptions", "x-route-name": "tenant.api.v1.webhook-subscriptions.index", "x-beta": true, "x-required-ability": "webhooks:manage", "responses": {"200": {"$ref": "#/components/responses/WebhookSubscriptionList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}},
      "post": {"operationId": "createWebhookSubscription", "x-route-name": "tenant.api.v1.webhook-subscriptions.store", "x-beta": true, "x-required-ability": "webhooks:manage", "description": "Requires an Idempotency-Key. A valid replay of the same logical operation returns the same subscription ID and original signing secret while the encrypted idempotency snapshot exists, after current authorization is rechecked. The secret is never available from GET or LIST.", "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookSubscriptionWrite"}}}}, "responses": {"201": {"$ref": "#/components/responses/WebhookSubscriptionCreated"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/WebhookScopeValidationProblem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}
    },
    "/webhook-subscriptions/{subscription_id}": {
      "parameters": [{"$ref": "#/components/parameters/SubscriptionId"}],
      "get": {"operationId": "getWebhookSubscription", "x-route-name": "tenant.api.v1.webhook-subscriptions.show", "x-beta": true, "x-required-ability": "webhooks:manage", "responses": {"200": {"$ref": "#/components/responses/WebhookSubscriptionOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}},
      "delete": {"operationId": "disableWebhookSubscription", "x-route-name": "tenant.api.v1.webhook-subscriptions.destroy", "x-beta": true, "x-required-ability": "webhooks:manage", "summary": "Idempotently disable a webhook subscription", "description": "Disables the authenticated User's subscription. A repeated request, an already-pruned subscription, or an identifier that is not owned by the authenticated User returns the same 204 response without exposing whether another User owns that identifier.", "responses": {"204": {"$ref": "#/components/responses/NoContent"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}}
    },
    "/webhook-resource-scopes/custom-forms": {"get": {
      "operationId": "listWebhookScopeCustomForms", "x-route-name": "tenant.api.v1.webhook-resource-scopes.custom-forms.index", "x-beta": true, "x-required-ability": "webhooks:manage",
      "summary": "Search Custom Forms eligible for webhook resource scope",
      "description": "Returns a bounded tenant-local list using the same coarse configuration eligibility as webhook subscription creation. Labels include safe Location context to disambiguate duplicate names. This operation does not authorize any event occurrence.",
      "parameters": [{"$ref": "#/components/parameters/ResourceSearch"}, {"$ref": "#/components/parameters/ResourceSearchLimit"}],
      "responses": {"200": {"$ref": "#/components/responses/WebhookResourceOptionList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/webhook-resource-scopes/review-flows": {"get": {
      "operationId": "listWebhookScopeReviewFlows", "x-route-name": "tenant.api.v1.webhook-resource-scopes.review-flows.index", "x-beta": true, "x-required-ability": "webhooks:manage",
      "summary": "Search Review Flows eligible for webhook resource scope",
      "description": "Returns a bounded tenant-local list across eligible Locations using the same coarse configuration eligibility as webhook subscription creation. Labels include safe Location context to disambiguate duplicate names. This operation does not authorize any event occurrence.",
      "parameters": [{"$ref": "#/components/parameters/ResourceSearch"}, {"$ref": "#/components/parameters/ResourceSearchLimit"}],
      "responses": {"200": {"$ref": "#/components/responses/WebhookResourceOptionList"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/webhook-event-samples": {"post": {
      "operationId": "sampleWebhookEvents", "x-route-name": "tenant.api.v1.webhook-event-samples.store", "x-beta": true, "x-required-ability": "webhooks:manage",
      "summary": "Retrieve recent authorized examples in the exact live webhook envelope",
      "description": "Returns at most ten retained real business occurrences for adapter setup. It is not a polling feed. webhook.test is synthetic-only and is rejected here.",
      "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookEventSampleRequest"}, "example": {"event_type": "custom_form.completed", "included_custom_form_ids": ["10000000-0000-4000-8000-000000000002"], "limit": 3}}}},
      "responses": {"200": {"$ref": "#/components/responses/WebhookEventSamplesOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/WebhookScopeValidationProblem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }},
    "/webhook-deliveries/resolve": {"post": {
      "operationId": "resolveWebhookDelivery", "x-route-name": "tenant.api.v1.webhook-deliveries.resolve", "x-beta": true, "x-required-ability": "webhooks:manage",
      "summary": "Verify a retained webhook delivery and hydrate its current resource",
      "description": "Platform-neutral adapter endpoint for webhook runtimes that cannot expose the exact original request bytes. The normal bearer credential must be the subscription's current creating credential. FSRevs locates the retained occurrence, delivery, and attempt; reconstructs the exact delivery payload; verifies the existing timestamp-and-payload HMAC; rechecks current event, scope, user, credential, and resource authorization; and returns the canonical event with the current authorized resource. Parsed webhook data is never trusted as proof.",
      "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookDeliveryResolutionRequest"}}}},
      "responses": {"200": {"$ref": "#/components/responses/WebhookDeliveryResolutionOk"}, "401": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/RateLimited"}, "503": {"$ref": "#/components/responses/Problem"}}
    }}
  },
  "components": {
    "securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "FSRevs API credential"}},
    "headers": {
      "ApiLifecycle": {"description": "Lifecycle of this API surface.", "schema": {"type": "string", "enum": ["beta"]}},
      "NoStore": {"description": "Public API responses are not cacheable; framework privacy directives may also be present.", "schema": {"type": "string", "enum": ["no-store", "no-store, private"]}},
      "IdempotencyReplayed": {"description": "Whether a completed idempotent result was replayed.", "schema": {"type": "string", "enum": ["true", "false"]}},
      "RetryAfter": {"description": "Whole seconds the client should wait before retrying.", "schema": {"type": "integer", "minimum": 0}},
      "RateLimitLimit": {"description": "Maximum requests permitted in the current rate-limit window.", "schema": {"type": "integer", "minimum": 1}},
      "RateLimitRemaining": {"description": "Requests remaining in the current rate-limit window.", "schema": {"type": "integer", "minimum": 0}},
      "RateLimitReset": {"description": "Unix timestamp when the current rate-limit window resets.", "schema": {"type": "integer", "minimum": 0}}
    },
    "parameters": {
      "IdempotencyKey": {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 255}},
      "UuidIdempotencyKey": {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "CustomerId": {"name": "customer_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "ExternalReferenceId": {"name": "external_reference_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "ReviewRequestId": {"name": "review_request_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "CustomFormRequestId": {"name": "custom_form_request_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "LocationId": {"name": "location_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "ReviewFlowId": {"name": "review_flow_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "CustomFormId": {"name": "custom_form_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "SubscriptionId": {"name": "subscription_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "OptionalLocationId": {"name": "location_id", "in": "query", "required": false, "schema": {"type": "string", "format": "uuid"}},
      "RequiredLocationIdQuery": {"name": "location_id", "in": "query", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "ResourceSearch": {"name": "search", "in": "query", "required": false, "description": "Case-insensitive resource name or Location label search.", "schema": {"type": "string", "maxLength": 100}},
      "ResourceSearchLimit": {"name": "limit", "in": "query", "required": false, "description": "Maximum options returned. Results are always bounded and never preloaded without a limit.", "schema": {"type": "integer", "minimum": 1, "maximum": 50, "default": 50}}
    },
    "responses": {
      "Problem": {"description": "Problem Details. A 503 with code integration_feature_unavailable means the global or tenant Integration Beta runtime is currently unavailable.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}},
      "RateLimited": {"description": "Too many API requests. Valid API credentials use a 60-request-per-minute tenant-and-principal quota; missing or invalid credentials use a separate tenant-and-source-IP pre-authentication quota. No controller, domain, or idempotency side effect occurs for the rejected request.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "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"}}, "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}, "example": {"type": "https://docs.fsrevs.com/problems/rate_limit_exceeded", "title": "Rate limit exceeded", "status": 429, "detail": "Too many API requests were made. Retry after the indicated interval.", "code": "rate_limit_exceeded"}}}},
      "WebhookScopeValidationProblem": {"description": "Validation failed. Scope errors include an empty Selected list, more than 100 submitted IDs, malformed UUIDs, event/scope incompatibility, or a wrong-type, cross-tenant, missing, inactive, or owner-inaccessible resource.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}, "examples": {"emptySelectedScope": {"summary": "Selected must contain at least one resource", "value": {"type": "https://docs.fsrevs.com/problems/validation_failed", "title": "Validation failed", "status": 422, "detail": "One or more request fields are invalid.", "code": "validation_failed", "errors": {"included_custom_form_ids": ["The included custom form ids field must have at least 1 items."]}}}, "incompatibleScope": {"summary": "Scope does not match the event type", "value": {"type": "https://docs.fsrevs.com/problems/validation_failed", "title": "Validation failed", "status": 422, "detail": "One or more request fields are invalid.", "code": "validation_failed", "errors": {"included_custom_form_ids": ["Custom Form scope requires the custom_form.completed event."]}}}, "unavailableResource": {"summary": "Resource is not eligible for this owner", "value": {"type": "https://docs.fsrevs.com/problems/validation_failed", "title": "Validation failed", "status": 422, "detail": "One or more request fields are invalid.", "code": "validation_failed", "errors": {"included_custom_form_ids": ["One or more selected resources are unavailable to the subscription owner."]}}}}}}},
      "ConnectionOk": {"description": "Authenticated workspace connection", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ConnectionEnvelope"}}}},
      "CustomerOk": {"description": "Customer", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomerEnvelope"}}}},
      "CustomerCreated": {"description": "Customer created", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomerEnvelope"}}}},
      "CustomerUpdated": {"description": "Customer updated", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomerEnvelope"}}}},
      "CustomerSynchronized": {"description": "Customer synchronization converged successfully. The result is created, updated, unchanged, or older_update_ignored.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomerSynchronizationEnvelope"}}}},
      "ExternalReferenceList": {"description": "Customer external references", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExternalReferenceListEnvelope"}}}},
      "ExternalReferenceCreated": {"description": "External reference created", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExternalReferenceEnvelope"}}}},
      "ReviewRequestOk": {"description": "Review Request", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ReviewRequestEnvelope"}}}},
      "ReviewRequestCreated": {"description": "Review Request created", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ReviewRequestSendEnvelope"}}}},
      "CustomFormRequestOk": {"description": "Custom Form request", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomFormRequestEnvelope"}}}},
      "CustomFormRequestCreated": {"description": "Custom Form request created", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomFormRequestSendEnvelope"}}}},
      "LocationList": {"description": "Authorized Locations", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LocationListEnvelope"}}}},
      "ReviewFlowList": {"description": "Available Review Flows", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ReviewFlowListEnvelope"}}}},
      "CustomFormList": {"description": "Available Custom Forms", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomFormListEnvelope"}}}},
      "UserOptionList": {"description": "Eligible Users", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOptionListEnvelope"}}}},
      "WebhookResourceOptionList": {"description": "Bounded resources eligible for webhook scope configuration by the authenticated User.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookResourceOptionListEnvelope"}}}},
      "WebhookSubscriptionList": {"description": "User-owned subscriptions", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookSubscriptionListEnvelope"}}}},
      "WebhookSubscriptionOk": {"description": "Webhook subscription", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookSubscriptionEnvelope"}}}},
      "WebhookSubscriptionCreated": {"description": "Created. The signing secret is available only during this logical idempotent creation, including an authorized replay while its protected response snapshot exists.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}, "Idempotency-Replayed": {"$ref": "#/components/headers/IdempotencyReplayed"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookSubscriptionCreatedEnvelope"}}}},
      "WebhookEventSamplesOk": {"description": "Recent authorized lean business-event examples in the exact live webhook envelope. No match returns an empty data array.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookEventSampleListEnvelope"}, "examples": {"customFormCompleted": {"value": {"data": [{"id": "10000000-0000-4000-8000-000000000000", "tenant_id": "example", "type": "custom_form.completed", "schema_version": 1, "details_included": false, "details_prepared_at": null, "occurred_at": "2026-08-20T12:00:00+00:00", "data": {"custom_form_request_id": "10000000-0000-4000-8000-000000000001", "custom_form_id": "10000000-0000-4000-8000-000000000002", "customer_id": "10000000-0000-4000-8000-000000000003", "location_id": "10000000-0000-4000-8000-000000000004", "creator_user_id": null, "sender_user_id": null, "employee_user_id": null, "completed_at": "2026-08-20T12:00:00+00:00"}}]}}, "reviewFeedbackRecorded": {"value": {"data": [{"id": "20000000-0000-4000-8000-000000000000", "tenant_id": "example", "type": "review_feedback.recorded", "schema_version": 1, "details_included": false, "details_prepared_at": null, "occurred_at": "2026-08-20T12:00:00+00:00", "data": {"review_request_id": "20000000-0000-4000-8000-000000000001", "customer_id": "20000000-0000-4000-8000-000000000002", "location_id": "20000000-0000-4000-8000-000000000003", "review_flow_id": "20000000-0000-4000-8000-000000000004", "creator_user_id": null, "sender_user_id": null, "employee_user_id": null, "rating": 80, "rating_scale": 100, "sentiment_widget": "stars", "feedback_present": true, "recorded_at": "2026-08-20T12:00:00+00:00"}}]}}, "reviewDestinationVisited": {"value": {"data": [{"id": "30000000-0000-4000-8000-000000000000", "tenant_id": "example", "type": "review_destination.visited", "schema_version": 1, "details_included": false, "details_prepared_at": null, "occurred_at": "2026-08-20T12:00:00+00:00", "data": {"review_request_id": "30000000-0000-4000-8000-000000000001", "review_site_id": 42, "review_site_name": "Example Reviews", "customer_id": "30000000-0000-4000-8000-000000000003", "location_id": "30000000-0000-4000-8000-000000000004", "review_flow_id": "30000000-0000-4000-8000-000000000005", "creator_user_id": null, "sender_user_id": null, "employee_user_id": null, "visited_at": "2026-08-20T12:00:00+00:00"}}]}}}}}},
      "WebhookDeliveryResolutionOk": {"description": "Verified retained webhook event and currently authorized resource.", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookDeliveryResolutionEnvelope"}}}},
      "NoContent": {"description": "Disabled", "headers": {"X-FSRevs-API-Lifecycle": {"$ref": "#/components/headers/ApiLifecycle"}, "Cache-Control": {"$ref": "#/components/headers/NoStore"}}}
    },
    "schemas": {
      "Problem": {"type": "object", "additionalProperties": true, "required": ["type", "title", "status", "detail", "code"], "properties": {
        "type": {"type": "string", "format": "uri"}, "title": {"type": "string"}, "status": {"type": "integer"}, "detail": {"type": "string"},
        "code": {"type": "string", "enum": ["validation_failed", "unauthenticated", "invalid_credential", "forbidden", "api_access_disabled", "tenant_host_not_found", "tenant_disabled", "resource_not_found", "customer_not_found", "customer_identity_ambiguous", "customer_identity_temporarily_unavailable", "customer_identity_conflict", "external_reference_conflict", "recent_request_conflict", "request_quota_exhausted", "external_channel_disabled", "external_send_rate_limited", "external_send_safety_limit_reached", "send_safety_unavailable", "idempotency_key_required", "idempotency_key_reuse", "rate_limit_exceeded", "integration_feature_unavailable", "internal_error"]},
        "errors": {"type": "object", "additionalProperties": {"type": "array", "items": {"type": "string"}}}
      }},
      "Connection": {"type": "object", "additionalProperties": false, "required": ["workspace_id", "workspace_name", "environment", "credential_abilities", "capabilities"], "properties": {"workspace_id": {"type": "string"}, "workspace_name": {"type": "string"}, "environment": {"type": "string"}, "credential_abilities": {"type": "array", "items": {"type": "string"}}, "capabilities": {"type": "object", "additionalProperties": false, "required": ["external_email_sending", "external_sms_sending"], "properties": {"external_email_sending": {"type": "boolean"}, "external_sms_sending": {"type": "boolean"}}}}},
      "ConnectionEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/Connection"}}},
      "CreateCustomer": {"type": "object", "additionalProperties": false, "minProperties": 1, "anyOf": [{"required": ["name"], "properties": {"name": {"type": "string", "minLength": 1}}}, {"required": ["first_name"], "properties": {"first_name": {"type": "string", "minLength": 1}}}, {"required": ["last_name"], "properties": {"last_name": {"type": "string", "minLength": 1}}}, {"required": ["email"], "properties": {"email": {"type": "string", "format": "email", "minLength": 1}}}, {"required": ["phone"], "properties": {"phone": {"type": "string", "minLength": 1}}}], "properties": {"name": {"type": "string", "nullable": true, "minLength": 1, "maxLength": 255}, "first_name": {"type": "string", "nullable": true, "minLength": 1, "maxLength": 255}, "last_name": {"type": "string", "nullable": true, "minLength": 1, "maxLength": 255}, "email": {"type": "string", "format": "email", "nullable": true, "minLength": 1, "maxLength": 255}, "phone": {"type": "string", "nullable": true, "minLength": 1}}},
      "SynchronizeCustomer": {"type": "object", "additionalProperties": false, "oneOf": [{"required": ["source", "external_id"], "anyOf": [{"required": ["name"], "properties": {"name": {"type": "string", "minLength": 1}}}, {"required": ["first_name"], "properties": {"first_name": {"type": "string", "minLength": 1}}}, {"required": ["last_name"], "properties": {"last_name": {"type": "string", "minLength": 1}}}, {"required": ["email"], "properties": {"email": {"type": "string", "format": "email", "minLength": 1}}}, {"required": ["phone"], "properties": {"phone": {"type": "string", "minLength": 1}}}], "properties": {"source": {"type": "string", "minLength": 1}, "external_id": {"type": "string", "minLength": 1}}}, {"anyOf": [{"required": ["email"], "properties": {"email": {"type": "string", "format": "email", "minLength": 1}}}, {"required": ["phone"], "properties": {"phone": {"type": "string", "minLength": 1}}}], "properties": {"source": {"type": "string", "nullable": true, "enum": [null]}, "external_id": {"type": "string", "nullable": true, "enum": [null]}, "source_updated_at": {"type": "string", "nullable": true, "enum": [null]}}}], "properties": {"source": {"type": "string", "nullable": true, "minLength": 1, "maxLength": 80, "description": "A durable source namespace supplied by the caller. FSRevs trims it, transliterates it with the application's standard ASCII helper, lowercases it, and collapses punctuation and whitespace to hyphens. The response returns the canonical value. Supply together with external_id, or omit both for one-off intake."}, "external_id": {"type": "string", "nullable": true, "minLength": 1, "maxLength": 191, "description": "Trimmed but otherwise opaque, case-preserving, and case-sensitive. Supply together with source, or omit both for one-off intake."}, "name": {"type": "string", "nullable": true, "maxLength": 255, "description": "Full name. When a corresponding explicit first_name or last_name is omitted, FSRevs derives that missing component from this value."}, "first_name": {"type": "string", "nullable": true, "maxLength": 255, "description": "Explicit first name. Takes precedence over the first name derived from name."}, "last_name": {"type": "string", "nullable": true, "maxLength": 255, "description": "Explicit last name. Takes precedence over the last name derived from name."}, "email": {"type": "string", "format": "email", "nullable": true, "maxLength": 255}, "phone": {"type": "string", "nullable": true}, "source_updated_at": {"type": "string", "format": "date-time", "nullable": true, "description": "Available only with source + external_id. A strictly older timezone-qualified RFC 3339 value is ignored successfully; equal or absent values process normally."}}},
      "UpdateCustomer": {"type": "object", "additionalProperties": false, "properties": {"name": {"type": "string", "nullable": true, "maxLength": 255}, "first_name": {"type": "string", "nullable": true, "maxLength": 255}, "last_name": {"type": "string", "nullable": true, "maxLength": 255}, "email": {"type": "string", "format": "email", "nullable": true, "maxLength": 255}, "phone": {"type": "string", "nullable": true}}},
      "Customer": {"type": "object", "additionalProperties": false, "required": ["id", "name", "first_name", "last_name", "email", "phone", "created_at", "updated_at"], "properties": {
        "id": {"type": "string", "format": "uuid"}, "name": {"type": "string", "nullable": true}, "first_name": {"type": "string", "nullable": true}, "last_name": {"type": "string", "nullable": true}, "email": {"type": "string", "format": "email", "nullable": true}, "phone": {"type": "string", "nullable": true}, "created_at": {"type": "string", "format": "date-time"}, "updated_at": {"type": "string", "format": "date-time"}, "external_references": {"type": "array", "items": {"$ref": "#/components/schemas/CustomerExternalReferenceSummary"}}
      }},
      "CustomerExternalReferenceSummary": {"type": "object", "additionalProperties": false, "required": ["id", "source", "external_id"], "properties": {"id": {"type": "string", "format": "uuid"}, "source": {"type": "string"}, "external_id": {"type": "string"}}},
      "CustomerEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/Customer"}}},
      "CustomerSynchronizationEnvelope": {"type": "object", "additionalProperties": false, "required": ["data", "result", "source", "external_id"], "properties": {"data": {"$ref": "#/components/schemas/Customer"}, "result": {"type": "string", "enum": ["created", "updated", "unchanged", "older_update_ignored"]}, "source": {"type": "string", "nullable": true}, "external_id": {"type": "string", "nullable": true}}},
      "ContactIdentityLookup": {"type": "object", "required": ["type"], "additionalProperties": false, "properties": {"type": {"type": "string", "enum": ["contact_identity"]}, "email": {"type": "string", "format": "email", "maxLength": 255}, "phone": {"type": "string"}}, "anyOf": [{"required": ["email"]}, {"required": ["phone"]}]},
      "ExternalReferenceLookup": {"type": "object", "required": ["type", "source", "external_id"], "additionalProperties": false, "properties": {"type": {"type": "string", "enum": ["external_reference"]}, "source": {"type": "string", "minLength": 1, "maxLength": 80, "description": "FSRevs canonicalizes this source using the same deterministic rule as customer synchronization. Use the returned canonical source for durable identity."}, "external_id": {"type": "string", "maxLength": 191, "description": "Trimmed but otherwise opaque, case-preserving, and case-sensitive. ABC123 and abc123 are distinct."}}},
      "ExternalReferenceWrite": {"type": "object", "required": ["source", "external_id"], "additionalProperties": false, "properties": {"source": {"type": "string", "minLength": 1, "maxLength": 80, "description": "FSRevs canonicalizes this source using the same deterministic rule as customer synchronization. The response returns the canonical source."}, "external_id": {"type": "string", "maxLength": 191, "description": "Trimmed but otherwise opaque, case-preserving, and case-sensitive."}}},
      "CustomerExternalReference": {"type": "object", "additionalProperties": false, "required": ["id", "customer_id", "source", "external_id"], "properties": {"id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid"}, "source": {"type": "string"}, "external_id": {"type": "string"}, "created_at": {"type": "string", "format": "date-time", "nullable": true}}},
      "ExternalReferenceEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/CustomerExternalReference"}}},
      "ExternalReferenceListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/CustomerExternalReference"}}}},
      "ExplicitSendChannels": {"type": "array", "minItems": 1, "maxItems": 2, "uniqueItems": true, "items": {"type": "string", "enum": ["email", "sms"]}, "description": "Deprecated V1 compatibility input. The server derives channels from nonblank explicit email and phone destinations."},
      "DeliveryChannelsSnapshot": {"type": "array", "minItems": 0, "maxItems": 2, "uniqueItems": true, "items": {"type": "string", "enum": ["email", "sms"]}, "description": "A response snapshot. Legacy and device-handoff requests may have no FSRevs infrastructure channel."},
      "SendReviewRequest": {"type": "object", "additionalProperties": false, "required": ["location_id", "review_flow_id"], "anyOf": [{"required": ["email"]}, {"required": ["phone"]}], "properties": {"location_id": {"type": "string", "format": "uuid"}, "review_flow_id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "name": {"type": "string", "nullable": true, "maxLength": 255}, "email": {"type": "string", "format": "email", "nullable": true, "maxLength": 255}, "phone": {"type": "string", "nullable": true}, "channels": {"$ref": "#/components/schemas/ExplicitSendChannels"}, "sender_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_id": {"type": "string", "format": "uuid", "nullable": true}}},
      "SendCustomFormRequest": {"type": "object", "additionalProperties": false, "required": ["custom_form_id"], "anyOf": [{"required": ["email"]}, {"required": ["phone"]}], "properties": {"custom_form_id": {"type": "string", "format": "uuid"}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "name": {"type": "string", "nullable": true, "maxLength": 255}, "email": {"type": "string", "format": "email", "nullable": true, "maxLength": 255}, "phone": {"type": "string", "nullable": true}, "channels": {"$ref": "#/components/schemas/ExplicitSendChannels"}, "sender_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_id": {"type": "string", "format": "uuid", "nullable": true}}},
      "RecipientSnapshot": {"type": "object", "additionalProperties": false, "required": ["name", "email", "phone"], "properties": {"name": {"type": "string", "nullable": true}, "email": {"type": "string", "format": "email", "nullable": true}, "phone": {"type": "string", "nullable": true}}},
      "DeliverySnapshot": {"type": "object", "additionalProperties": false, "required": ["channels", "destinations"], "properties": {"channels": {"$ref": "#/components/schemas/DeliveryChannelsSnapshot"}, "destinations": {"type": "object", "additionalProperties": false, "required": ["email", "sms"], "properties": {"email": {"type": "string", "nullable": true}, "sms": {"type": "string", "nullable": true}}}}},
      "ReviewRequest": {"type": "object", "additionalProperties": false, "required": ["id", "customer_id", "location_id", "review_flow_id", "creator_user_id", "sender_user_id", "employee_user_id", "recipient", "delivery", "origin", "is_complete", "rating", "rating_scale", "sentiment_widget", "feedback", "feedback_present", "has_review", "created_at", "completed_at"], "properties": {"id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid"}, "review_flow_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "recipient": {"$ref": "#/components/schemas/RecipientSnapshot"}, "delivery": {"$ref": "#/components/schemas/DeliverySnapshot"}, "origin": {"type": "string", "enum": ["system", "api", "customer"]}, "is_complete": {"type": "boolean"}, "rating": {"type": "integer", "minimum": 0, "maximum": 100, "nullable": true}, "rating_scale": {"type": "integer", "enum": [100]}, "sentiment_widget": {"$ref": "#/components/schemas/SentimentWidget"}, "feedback": {"type": "string", "nullable": true, "description": "Current written feedback. WYSIWYG HTML is returned unchanged."}, "feedback_present": {"type": "boolean"}, "has_review": {"type": "boolean"}, "created_at": {"type": "string", "format": "date-time"}, "completed_at": {"type": "string", "format": "date-time", "nullable": true}}},
      "CustomFormRequest": {"type": "object", "additionalProperties": false, "required": ["id", "custom_form_id", "customer_id", "location_id", "creator_user_id", "sender_user_id", "employee_user_id", "recipient", "delivery", "origin", "is_complete", "completed_at", "answers", "created_at"], "properties": {"id": {"type": "string", "format": "uuid"}, "custom_form_id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "recipient": {"$ref": "#/components/schemas/RecipientSnapshot"}, "delivery": {"$ref": "#/components/schemas/DeliverySnapshot"}, "origin": {"type": "string", "enum": ["system", "api", "customer"]}, "is_complete": {"type": "boolean"}, "completed_at": {"type": "string", "format": "date-time", "nullable": true}, "answers": {"anyOf": [{"type": "object", "additionalProperties": true}, {"type": "array", "items": {}}], "description": "Authorized dynamic answer data. Attachments are excluded."}, "created_at": {"type": "string", "format": "date-time"}}},
      "CustomerResolution": {"type": "object", "additionalProperties": false, "required": ["result", "enriched_fields"], "properties": {"result": {"type": "string", "enum": ["selected", "matched", "created"]}, "enriched_fields": {"type": "array", "items": {"type": "string", "enum": ["name", "first_name", "last_name", "email", "phone"]}}}},
      "ReviewRequestEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/ReviewRequest"}}},
      "ReviewRequestSendEnvelope": {"type": "object", "additionalProperties": false, "required": ["data", "customer_resolution"], "properties": {"data": {"$ref": "#/components/schemas/ReviewRequest"}, "customer_resolution": {"$ref": "#/components/schemas/CustomerResolution"}}},
      "CustomFormRequestEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/CustomFormRequest"}}},
      "CustomFormRequestSendEnvelope": {"type": "object", "additionalProperties": false, "required": ["data", "customer_resolution"], "properties": {"data": {"$ref": "#/components/schemas/CustomFormRequest"}, "customer_resolution": {"$ref": "#/components/schemas/CustomerResolution"}}},
      "Location": {"type": "object", "additionalProperties": false, "required": ["id", "name"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}}},
      "ReviewFlow": {"type": "object", "additionalProperties": false, "required": ["id", "name", "location_id"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}, "location_id": {"type": "string", "format": "uuid"}}},
      "CustomForm": {"type": "object", "additionalProperties": false, "required": ["id", "name", "location_id"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}, "location_id": {"type": "string", "format": "uuid", "nullable": true}}},
      "UserOption": {"type": "object", "additionalProperties": false, "required": ["id", "name"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}}},
      "LocationListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/Location"}}}},
      "ReviewFlowListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/ReviewFlow"}}}},
      "CustomFormListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/CustomForm"}}}},
      "UserOptionListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/UserOption"}}}},
      "WebhookResourceOption": {"type": "object", "additionalProperties": false, "required": ["id", "name"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string", "description": "Friendly resource label with safe Location context, or All locations for a global resource."}}},
      "WebhookResourceOptionListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "maxItems": 50, "items": {"$ref": "#/components/schemas/WebhookResourceOption"}}}},
      "PublicEventType": {"type": "string", "enum": ["custom_form.completed", "customer.created", "customer.updated", "review_request.sent", "review_response.completed", "review_feedback.recorded", "review_destination.visited"]},
      "WebhookEventEnvelope": {"oneOf": [{"$ref": "#/components/schemas/CustomFormCompletedEnvelope"}, {"$ref": "#/components/schemas/CustomerCreatedEnvelope"}, {"$ref": "#/components/schemas/CustomerUpdatedEnvelope"}, {"$ref": "#/components/schemas/ReviewRequestSentEnvelope"}, {"$ref": "#/components/schemas/ReviewResponseCompletedEnvelope"}, {"$ref": "#/components/schemas/ReviewFeedbackRecordedEnvelope"}, {"$ref": "#/components/schemas/ReviewDestinationVisitedEnvelope"}], "discriminator": {"propertyName": "type", "mapping": {"custom_form.completed": "#/components/schemas/CustomFormCompletedEnvelope", "customer.created": "#/components/schemas/CustomerCreatedEnvelope", "customer.updated": "#/components/schemas/CustomerUpdatedEnvelope", "review_request.sent": "#/components/schemas/ReviewRequestSentEnvelope", "review_response.completed": "#/components/schemas/ReviewResponseCompletedEnvelope", "review_feedback.recorded": "#/components/schemas/ReviewFeedbackRecordedEnvelope", "review_destination.visited": "#/components/schemas/ReviewDestinationVisitedEnvelope"}}},
      "WebhookRecipientDetails": {"type": "object", "additionalProperties": false, "required": ["name", "first_name", "last_name", "email", "phone"], "properties": {"name": {"type": "string", "nullable": true}, "first_name": {"type": "string", "nullable": true}, "last_name": {"type": "string", "nullable": true}, "email": {"type": "string", "nullable": true}, "phone": {"type": "string", "nullable": true}}},
      "WebhookCustomerDetails": {"type": "object", "nullable": true, "additionalProperties": false, "required": ["id", "name", "first_name", "last_name", "email", "phone"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string", "nullable": true}, "first_name": {"type": "string", "nullable": true}, "last_name": {"type": "string", "nullable": true}, "email": {"type": "string", "nullable": true}, "phone": {"type": "string", "nullable": true}}},
      "WebhookStaffDetails": {"type": "object", "nullable": true, "additionalProperties": false, "required": ["id", "name"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}}},
      "WebhookUploadDetails": {"type": "object", "additionalProperties": false, "required": ["uuid", "field_key", "file_name", "mime_type", "size_bytes"], "properties": {"uuid": {"type": "string", "format": "uuid", "nullable": true}, "field_key": {"type": "string", "nullable": true}, "file_name": {"type": "string"}, "mime_type": {"type": "string", "nullable": true}, "size_bytes": {"type": "integer"}}},
      "WebhookCommonDetails": {"type": "object", "additionalProperties": false, "required": ["recipient", "customer", "creator", "sender", "employee"], "properties": {"recipient": {"$ref": "#/components/schemas/WebhookRecipientDetails"}, "customer": {"$ref": "#/components/schemas/WebhookCustomerDetails"}, "creator": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "sender": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "employee": {"$ref": "#/components/schemas/WebhookStaffDetails"}}},
      "CustomFormWebhookDetails": {"type": "object", "additionalProperties": false, "required": ["recipient", "customer", "creator", "sender", "employee", "answers", "uploads"], "properties": {"recipient": {"$ref": "#/components/schemas/WebhookRecipientDetails"}, "customer": {"$ref": "#/components/schemas/WebhookCustomerDetails"}, "creator": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "sender": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "employee": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "answers": {"type": "object", "additionalProperties": true}, "uploads": {"type": "array", "items": {"$ref": "#/components/schemas/WebhookUploadDetails"}}}},
      "ReviewFeedbackWebhookDetails": {"type": "object", "additionalProperties": false, "required": ["recipient", "customer", "creator", "sender", "employee", "feedback"], "properties": {"recipient": {"$ref": "#/components/schemas/WebhookRecipientDetails"}, "customer": {"$ref": "#/components/schemas/WebhookCustomerDetails"}, "creator": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "sender": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "employee": {"$ref": "#/components/schemas/WebhookStaffDetails"}, "feedback": {"type": "string", "nullable": true}}},
      "CustomFormCompletedData": {"type": "object", "additionalProperties": false, "required": ["custom_form_request_id", "custom_form_id", "customer_id", "location_id", "creator_user_id", "sender_user_id", "employee_user_id", "completed_at"], "properties": {"custom_form_request_id": {"type": "string", "format": "uuid"}, "custom_form_id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "completed_at": {"type": "string", "format": "date-time"}, "details": {"$ref": "#/components/schemas/CustomFormWebhookDetails"}}},
      "CustomerEventSource": {"type": "string", "enum": ["api", "portal", "review_request", "custom_form_request", "review_response", "custom_form_submission", "merge"]},
      "ReviewRequestOrigin": {"type": "string", "enum": ["system", "api"]},
      "SentimentWidget": {"type": "string", "enum": ["none", "stars", "numeric", "customer_recommendation_score", "stars_basic"], "description": "Stable public enum. Missing stored values are projected as none by current hydration and public-event producers."},
      "ReviewRequestDeliveryMethod": {"type": "string", "enum": ["fsrevs_delivery", "device_handoff"], "description": "How FSRevs durably handled the Review Request. fsrevs_delivery means FSRevs queued delivery; device_handoff means FSRevs prepared it for the sender's email or SMS app. Neither value confirms provider or device delivery."},
      "CustomerCreatedData": {"type": "object", "additionalProperties": false, "required": ["customer_id", "creation_source", "created_at"], "properties": {"customer_id": {"type": "string", "format": "uuid"}, "creation_source": {"$ref": "#/components/schemas/CustomerEventSource"}, "created_at": {"type": "string", "format": "date-time"}}},
      "CustomerUpdatedData": {"type": "object", "additionalProperties": false, "required": ["customer_id", "changed_fields", "change_source", "updated_at"], "properties": {"customer_id": {"type": "string", "format": "uuid"}, "changed_fields": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "enum": ["name", "first_name", "last_name", "email", "phone"]}}, "change_source": {"$ref": "#/components/schemas/CustomerEventSource"}, "updated_at": {"type": "string", "format": "date-time"}}},
      "ReviewRequestSentData": {"type": "object", "additionalProperties": false, "required": ["review_request_id", "customer_id", "location_id", "review_flow_id", "creator_user_id", "sender_user_id", "employee_user_id", "channels", "origin", "delivery_method", "sentiment_widget", "sent_at"], "properties": {"review_request_id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "review_flow_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "channels": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "enum": ["email", "sms"]}}, "origin": {"$ref": "#/components/schemas/ReviewRequestOrigin"}, "delivery_method": {"$ref": "#/components/schemas/ReviewRequestDeliveryMethod"}, "sentiment_widget": {"$ref": "#/components/schemas/SentimentWidget"}, "sent_at": {"type": "string", "format": "date-time", "description": "For fsrevs_delivery, when the request and message intents were durably accepted. For device_handoff, when the Message was durably prepared for the sender's app. This is not confirmed provider or device delivery."}}},
      "ReviewResponseCompletedData": {"type": "object", "additionalProperties": false, "required": ["review_request_id", "customer_id", "location_id", "review_flow_id", "creator_user_id", "sender_user_id", "employee_user_id", "rating", "rating_scale", "sentiment_widget", "feedback_present", "completed_at"], "properties": {"review_request_id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "review_flow_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "rating": {"type": "integer", "minimum": 0, "maximum": 100, "nullable": true}, "rating_scale": {"type": "integer", "enum": [100]}, "sentiment_widget": {"$ref": "#/components/schemas/SentimentWidget"}, "feedback_present": {"type": "boolean"}, "completed_at": {"type": "string", "format": "date-time"}}},
      "ReviewFeedbackRecordedData": {"type": "object", "additionalProperties": false, "required": ["review_request_id", "customer_id", "location_id", "review_flow_id", "creator_user_id", "sender_user_id", "employee_user_id", "rating", "rating_scale", "sentiment_widget", "feedback_present", "recorded_at"], "properties": {"review_request_id": {"type": "string", "format": "uuid"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "review_flow_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "rating": {"type": "integer", "minimum": 0, "maximum": 100, "nullable": true}, "rating_scale": {"type": "integer", "enum": [100]}, "sentiment_widget": {"$ref": "#/components/schemas/SentimentWidget"}, "feedback_present": {"type": "boolean"}, "recorded_at": {"type": "string", "format": "date-time"}, "details": {"$ref": "#/components/schemas/ReviewFeedbackWebhookDetails"}}},
      "ReviewDestinationVisitedData": {"type": "object", "additionalProperties": false, "required": ["review_request_id", "review_site_id", "review_site_name", "customer_id", "location_id", "review_flow_id", "creator_user_id", "sender_user_id", "employee_user_id", "visited_at"], "properties": {"review_request_id": {"type": "string", "format": "uuid"}, "review_site_id": {"type": "integer"}, "review_site_name": {"type": "string"}, "customer_id": {"type": "string", "format": "uuid", "nullable": true}, "location_id": {"type": "string", "format": "uuid", "nullable": true}, "review_flow_id": {"type": "string", "format": "uuid", "nullable": true}, "creator_user_id": {"type": "string", "format": "uuid", "nullable": true}, "sender_user_id": {"type": "string", "format": "uuid", "nullable": true}, "employee_user_id": {"type": "string", "format": "uuid", "nullable": true}, "visited_at": {"type": "string", "format": "date-time"}, "details": {"$ref": "#/components/schemas/WebhookCommonDetails"}}},
      "CustomFormCompletedEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["custom_form.completed"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "description": "True exactly when data.details is present."}, "details_prepared_at": {"type": "string", "format": "date-time", "nullable": true, "description": "When sensitive data.details values were prepared for this occurrence. Null when details_included is false."}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/CustomFormCompletedData"}}},
      "CustomerCreatedEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["customer.created"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "enum": [false]}, "details_prepared_at": {"type": "string", "nullable": true, "enum": [null]}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/CustomerCreatedData"}}},
      "CustomerUpdatedEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["customer.updated"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "enum": [false]}, "details_prepared_at": {"type": "string", "nullable": true, "enum": [null]}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/CustomerUpdatedData"}}},
      "ReviewRequestSentEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["review_request.sent"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "enum": [false]}, "details_prepared_at": {"type": "string", "nullable": true, "enum": [null]}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/ReviewRequestSentData"}}},
      "ReviewResponseCompletedEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["review_response.completed"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "enum": [false]}, "details_prepared_at": {"type": "string", "nullable": true, "enum": [null]}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/ReviewResponseCompletedData"}}},
      "ReviewFeedbackRecordedEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["review_feedback.recorded"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "description": "True exactly when data.details is present."}, "details_prepared_at": {"type": "string", "format": "date-time", "nullable": true, "description": "When sensitive data.details values were prepared for this occurrence. Null when details_included is false."}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/ReviewFeedbackRecordedData"}}},
      "ReviewDestinationVisitedEnvelope": {"type": "object", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["review_destination.visited"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "description": "True exactly when data.details is present."}, "details_prepared_at": {"type": "string", "format": "date-time", "nullable": true, "description": "When sensitive data.details values were prepared for this occurrence. Null when details_included is false."}, "occurred_at": {"type": "string", "format": "date-time", "description": "When the immutable event occurred."}, "data": {"$ref": "#/components/schemas/ReviewDestinationVisitedData"}}},
      "WebhookTestEnvelope": {"type": "object", "description": "Synthetic portal-only diagnostic envelope. webhook.test is not subscribable, retained, fanned out, sampled, or exposed as an automation trigger.", "additionalProperties": false, "required": ["id", "tenant_id", "type", "schema_version", "details_included", "details_prepared_at", "occurred_at", "test", "data"], "properties": {"id": {"type": "string", "format": "uuid"}, "tenant_id": {"type": "string"}, "type": {"type": "string", "enum": ["webhook.test"]}, "schema_version": {"type": "integer", "enum": [1]}, "details_included": {"type": "boolean", "enum": [false]}, "details_prepared_at": {"type": "string", "nullable": true, "enum": [null]}, "occurred_at": {"type": "string", "format": "date-time"}, "test": {"type": "boolean", "enum": [true]}, "data": {"type": "object", "additionalProperties": false, "required": ["subscription_id", "message"], "properties": {"subscription_id": {"type": "string", "format": "uuid"}, "message": {"type": "string"}}}}},
      "WebhookEventSampleRequest": {"type": "object", "additionalProperties": false, "required": ["event_type"], "description": "Returns bounded, currently authorized samples. Scope fields are optional: null or omission means all resources for the event type; a non-empty list narrows samples. A Custom Form scope is valid only with custom_form.completed. A Review Flow scope is valid only with a Review Flow event.", "properties": {"event_type": {"$ref": "#/components/schemas/PublicEventType"}, "included_custom_form_ids": {"type": "array", "nullable": true, "minItems": 1, "maxItems": 100, "items": {"type": "string", "format": "uuid"}, "description": "Optional explicit Custom Form include-list. Null or omitted means all current and future Custom Forms. The server canonicalizes, deduplicates, and sorts the list."}, "included_review_flow_ids": {"type": "array", "nullable": true, "minItems": 1, "maxItems": 100, "items": {"type": "string", "format": "uuid"}, "description": "Optional explicit Review Flow include-list. Null or omitted means all current and future Review Flows. The server canonicalizes, deduplicates, and sorts the list."}, "limit": {"type": "integer", "minimum": 1, "maximum": 10, "default": 3}}},
      "WebhookEventSampleListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "maxItems": 10, "items": {"$ref": "#/components/schemas/WebhookEventEnvelope"}}}},
      "WebhookDeliveryResolutionRequest": {"type": "object", "additionalProperties": false, "required": ["subscription_id", "event_id", "timestamp", "signature"], "description": "Proof copied from the recorded outbound delivery headers. X-FSRevs-Subscription-Id supplies subscription_id only as lookup metadata; FSRevs verifies the event, attempt, exact reconstructed payload, signature, creating credential, and current authorization before returning data.", "properties": {"subscription_id": {"type": "string", "format": "uuid"}, "event_id": {"type": "string", "format": "uuid"}, "timestamp": {"type": "integer", "minimum": 1, "maximum": 4102444800}, "signature": {"type": "string", "pattern": "^v1=[a-f0-9]{64}$"}}},
      "WebhookDeliveryResolution": {"type": "object", "additionalProperties": false, "required": ["event", "resource_type", "resource"], "properties": {"event": {"$ref": "#/components/schemas/WebhookEventEnvelope"}, "resource_type": {"type": "string", "enum": ["customer", "review_request", "custom_form_request"]}, "resource": {"oneOf": [{"$ref": "#/components/schemas/Customer"}, {"$ref": "#/components/schemas/ReviewRequest"}, {"$ref": "#/components/schemas/CustomFormRequest"}]}}},
      "WebhookDeliveryResolutionEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/WebhookDeliveryResolution"}}},
      "WebhookSubscriptionWrite": {"type": "object", "required": ["name", "endpoint_url", "event_types"], "additionalProperties": false, "description": "Resource scope only narrows delivery candidates; it never grants access. Exact occurrence authorization is evaluated again when FSRevs fans out and dispatches the webhook. API-created subscriptions require webhooks:manage plus each selected event's resource-read ability. Scope compatibility and inaccessible, inactive, missing, cross-tenant, or wrong-type IDs are rejected with 422.", "properties": {"name": {"type": "string", "minLength": 1, "maxLength": 100}, "endpoint_url": {"type": "string", "format": "uri", "maxLength": 2048, "pattern": "^https://[^/@:]+(?:\\.[^/@:]+)+(?:/|$)", "description": "Public DNS hostname over HTTPS with port omitted or 443. IP literals, userinfo, fragments, redirects, internal hosts, and any hostname resolving partly or wholly to a non-public address are rejected."}, "event_types": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"$ref": "#/components/schemas/PublicEventType"}}, "include_sensitive_details": {"type": "boolean", "default": false, "description": "Opt in to immutable sensitive detail snapshots only for event types whose catalog contract supports them. Files and download URLs are never included. Native adapters use lean delivery plus authenticated hydration."}, "included_custom_form_ids": {"type": "array", "nullable": true, "minItems": 1, "maxItems": 100, "items": {"type": "string", "format": "uuid"}, "description": "Optional explicit Custom Form include-list for custom_form.completed. Null or omitted means all current and future Custom Forms. An empty list is invalid. The server canonicalizes, deduplicates, and sorts the list before idempotency fingerprinting."}, "included_review_flow_ids": {"type": "array", "nullable": true, "minItems": 1, "maxItems": 100, "items": {"type": "string", "format": "uuid"}, "description": "Optional explicit Review Flow include-list shared by review_request.sent, review_response.completed, review_feedback.recorded, and review_destination.visited. Null or omitted means all current and future Review Flows. An empty list is invalid. The server canonicalizes, deduplicates, and sorts the list before idempotency fingerprinting."}}},
      "WebhookSubscription": {"type": "object", "additionalProperties": false, "required": ["id", "name", "endpoint_url", "event_types", "include_sensitive_details", "included_custom_form_ids", "included_review_flow_ids", "status", "created_at", "disabled_at"], "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}, "endpoint_url": {"type": "string", "format": "uri"}, "event_types": {"type": "array", "items": {"$ref": "#/components/schemas/PublicEventType"}}, "include_sensitive_details": {"type": "boolean"}, "included_custom_form_ids": {"type": "array", "nullable": true, "minItems": 1, "maxItems": 100, "uniqueItems": true, "items": {"type": "string", "format": "uuid"}, "description": "Canonical sorted include-list, or null for all current and future Custom Forms."}, "included_review_flow_ids": {"type": "array", "nullable": true, "minItems": 1, "maxItems": 100, "uniqueItems": true, "items": {"type": "string", "format": "uuid"}, "description": "Canonical sorted include-list, or null for all current and future Review Flows."}, "status": {"type": "string", "enum": ["active", "disabled"]}, "created_at": {"type": "string", "format": "date-time", "nullable": true}, "disabled_at": {"type": "string", "format": "date-time", "nullable": true}}},
      "WebhookSubscriptionEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/WebhookSubscription"}}},
      "WebhookSubscriptionCreatedEnvelope": {"type": "object", "additionalProperties": false, "required": ["data", "signing_secret"], "properties": {"data": {"$ref": "#/components/schemas/WebhookSubscription"}, "signing_secret": {"type": "string", "description": "Returned only during the original logical idempotent creation, including an authorized replay while the encrypted response snapshot exists. It is never returned by GET or LIST."}}},
      "WebhookSubscriptionListEnvelope": {"type": "object", "additionalProperties": false, "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/WebhookSubscription"}}}}
    }
  }
}
