{
  "openapi": "3.0.3",
  "info": {
    "title": "EdgeMQ API",
    "description": "Manage ingest endpoints, S3 destinations, schema views, and monitor data delivery.",
    "version": "1.0.0",
    "contact": {
      "name": "EdgeMQ Support",
      "url": "https://edge.mq/docs"
    }
  },
  "servers": [
    {
      "url": "https://api.edge.mq",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "auth",
      "description": "Authentication and token management"
    },
    {
      "name": "endpoints",
      "description": "Ingest endpoint lifecycle"
    },
    {
      "name": "destinations",
      "description": "S3 destination configuration and validation"
    },
    {
      "name": "views",
      "description": "Schema view definitions and lifecycle"
    },
    {
      "name": "bindings",
      "description": "View-to-endpoint bindings"
    },
    {
      "name": "observability",
      "description": "Delivery confirmation, data quality metrics, and dead letter queue"
    }
  ],
  "paths": {
    "/v1/token/exchange": {
      "post": {
        "operationId": "exchangeApiKey",
        "tags": [
          "auth"
        ],
        "summary": "Exchange API key for access token",
        "description": "Exchange a long-lived API key for a short-lived JWT access token. The returned token is scoped to a single permission level and should be used in the Authorization header for all subsequent API calls.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenExchangeRequest"
              },
              "example": {
                "api_key": "emq_live_a1b2c3d4e5f6g7h8i9j0",
                "scope": "admin",
                "ttl": "15m"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenExchangeResponse"
                },
                "example": {
                  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZXMiOlsiYWRtaW4iXSwiaWF0IjoxNzA1MzEyNjAwLCJleHAiOjE3MDUzMTM1MDB9.signature",
                  "token_type": "Bearer",
                  "expires_in": 900
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "error": "unauthorized",
                  "detail": "The provided API key is invalid or has been revoked."
                }
              }
            }
          },
          "403": {
            "description": "Scope not permitted for this API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/forbidden",
                  "title": "Forbidden",
                  "status": 403,
                  "error": "forbidden",
                  "detail": "scope not permitted"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/endpoints": {
      "post": {
        "operationId": "createEndpoint",
        "tags": [
          "endpoints"
        ],
        "summary": "Create an ingest endpoint",
        "description": "Provision a new ingest endpoint in the specified region. Optionally bind an S3 destination at creation time. DNS records and TLS certificates are provisioned automatically. The endpoint transitions from `deploying` to `active` within 2-5 minutes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateEndpointRequest"
              },
              "example": {
                "region": "iad"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Endpoint"
                },
                "example": {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "region": "iad",
                  "status": "deploying",
                  "destination_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                  "destination_name": "Production S3",
                  "destination_status": "valid",
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "dns_ipv4": [],
                  "dns_ipv6": [],
                  "segment_bytes": 33554432,
                  "segment_max_age_seconds": 60,
                  "output_formats": [
                    "segments",
                    "parquet"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "get": {
        "operationId": "listEndpoints",
        "tags": [
          "endpoints"
        ],
        "summary": "List your ingest endpoints",
        "description": "Returns a paginated list of your ingest endpoints. Filter by status to find active, deploying, or degraded endpoints. Status is computed in real time from node health, DNS, and certificate state.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by endpoint status. Use `all` to include every status.",
            "schema": {
              "type": "string",
              "enum": [
                "deploying",
                "active",
                "degraded",
                "paused",
                "draining",
                "failed",
                "all"
              ],
              "default": "all"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of items to return (1-200).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of items to skip for pagination.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointList"
                },
                "example": {
                  "items": [
                    {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "region": "iad",
                      "status": "active",
                      "destination_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                      "destination_name": "Production S3",
                      "destination_status": "valid",
                      "created_at": "2026-01-15T10:30:00.000Z",
                      "dns_ipv4": [
                        "149.248.213.170"
                      ],
                      "dns_ipv6": [
                        "2001:19f0:ac01:1d2b:5400:04ff:fe5e:39a1"
                      ],
                      "segment_bytes": 33554432,
                      "segment_max_age_seconds": 60,
                      "output_formats": [
                        "segments",
                        "parquet"
                      ]
                    }
                  ],
                  "total": 1,
                  "limit": 50,
                  "offset": 0,
                  "next_offset": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/endpoints/{id}": {
      "patch": {
        "operationId": "updateEndpoint",
        "tags": [
          "endpoints"
        ],
        "summary": "Update an endpoint",
        "description": "Update endpoint settings such as the bound S3 destination, WAL segment size, segment max age, or output formats. Changes to segment settings and output formats trigger a config refresh on ingest nodes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateEndpointRequest"
              },
              "example": {
                "segment_bytes": 16777216,
                "output_formats": [
                  "segments",
                  "parquet"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Endpoint updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Endpoint"
                },
                "example": {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "region": "iad",
                  "status": "active",
                  "destination_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                  "destination_name": "Production S3",
                  "destination_status": "valid",
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "dns_ipv4": [
                    "149.248.213.170"
                  ],
                  "dns_ipv6": [
                    "2001:19f0:ac01:1d2b:5400:04ff:fe5e:39a1"
                  ],
                  "segment_bytes": 16777216,
                  "segment_max_age_seconds": 60,
                  "output_formats": [
                    "segments",
                    "parquet",
                    "parquet_views"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/destinations": {
      "post": {
        "operationId": "createDestination",
        "tags": [
          "destinations"
        ],
        "summary": "Create an S3 destination",
        "description": "Register a new S3 destination with IAM role-based access. EdgeMQ assumes the provided IAM role using an external ID unique to your account. After creation, call the validate endpoint to verify connectivity.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDestinationRequest"
              },
              "example": {
                "name": "Analytics Data Lake",
                "bucket": "acme-data-lake",
                "prefix": "events/prod",
                "region": "us-east-1",
                "role_arn": "arn:aws:iam::123456789012:role/EdgeMqDelivery",
                "kms_key_arn": null,
                "path_style": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Destination created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Destination"
                },
                "example": {
                  "id": "1f1a9a3d-3a2e-4b1a-9f6a-8f6d8a7e3c1b",
                  "name": "Analytics Data Lake",
                  "bucket": "acme-data-lake",
                  "prefix": "events/prod",
                  "region": "us-east-1",
                  "role_arn": "arn:aws:iam::123456789012:role/EdgeMqDelivery",
                  "external_id": "edgemq-abcdef0123456789",
                  "kms_key_arn": null,
                  "path_style": false,
                  "status": "pending",
                  "validated_at": null,
                  "last_error": null,
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "updated_at": "2026-01-15T10:30:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Destination name already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/conflict",
                  "title": "Conflict",
                  "status": 409,
                  "error": "conflict",
                  "detail": "A destination with this name already exists in your account."
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "get": {
        "operationId": "listDestinations",
        "tags": [
          "destinations"
        ],
        "summary": "List your S3 destinations",
        "description": "Returns a paginated list of your S3 destinations. Each destination includes a `ref_count` indicating how many endpoints reference it.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of items to return (1-200).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of items to skip for pagination.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of destinations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DestinationList"
                },
                "example": {
                  "items": [
                    {
                      "id": "1f1a9a3d-3a2e-4b1a-9f6a-8f6d8a7e3c1b",
                      "name": "Analytics Data Lake",
                      "bucket": "acme-data-lake",
                      "prefix": "events/prod",
                      "region": "us-east-1",
                      "role_arn": "arn:aws:iam::123456789012:role/EdgeMqDelivery",
                      "external_id": "edgemq-abcdef0123456789",
                      "kms_key_arn": null,
                      "path_style": false,
                      "status": "valid",
                      "validated_at": "2026-01-15T10:35:00.000Z",
                      "last_error": null,
                      "created_at": "2026-01-15T10:30:00.000Z",
                      "updated_at": "2026-01-15T10:35:00.000Z",
                      "ref_count": 2
                    }
                  ],
                  "total": 1,
                  "limit": 50,
                  "offset": 0,
                  "next_offset": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/destinations/{id}": {
      "get": {
        "operationId": "getDestination",
        "tags": [
          "destinations"
        ],
        "summary": "Get a destination",
        "description": "Retrieve the full details of an S3 destination including its validation status and the number of endpoints referencing it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Destination ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Destination details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DestinationWithRefCount"
                },
                "example": {
                  "id": "1f1a9a3d-3a2e-4b1a-9f6a-8f6d8a7e3c1b",
                  "name": "Analytics Data Lake",
                  "bucket": "acme-data-lake",
                  "prefix": "events/prod",
                  "region": "us-east-1",
                  "role_arn": "arn:aws:iam::123456789012:role/EdgeMqDelivery",
                  "external_id": "edgemq-abcdef0123456789",
                  "kms_key_arn": null,
                  "path_style": false,
                  "status": "valid",
                  "validated_at": "2026-01-15T10:35:00.000Z",
                  "last_error": null,
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "updated_at": "2026-01-15T10:35:00.000Z",
                  "ref_count": 2
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateDestination",
        "tags": [
          "destinations"
        ],
        "summary": "Update a destination",
        "description": "Update S3 destination settings. When critical fields (bucket, prefix, region, role_arn, kms_key_arn, path_style) are changed, the destination status resets to `pending` and must be re-validated.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Destination ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDestinationRequest"
              },
              "example": {
                "name": "Analytics Data Lake v2",
                "prefix": "events/prod/v2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Destination updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Destination"
                },
                "example": {
                  "id": "1f1a9a3d-3a2e-4b1a-9f6a-8f6d8a7e3c1b",
                  "name": "Analytics Data Lake v2",
                  "bucket": "acme-data-lake",
                  "prefix": "events/prod/v2",
                  "region": "us-east-1",
                  "role_arn": "arn:aws:iam::123456789012:role/EdgeMqDelivery",
                  "external_id": "edgemq-abcdef0123456789",
                  "kms_key_arn": null,
                  "path_style": false,
                  "status": "pending",
                  "validated_at": null,
                  "last_error": null,
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "updated_at": "2026-01-15T11:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "operationId": "deleteDestination",
        "tags": [
          "destinations"
        ],
        "summary": "Delete a destination",
        "description": "Permanently delete an S3 destination. The destination cannot be deleted if it is still referenced by one or more endpoints. Unbind the destination from all endpoints first.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Destination ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Destination deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Destination is referenced by endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/conflict",
                  "title": "Conflict",
                  "status": 409,
                  "error": "conflict",
                  "detail": "destination is referenced by one or more endpoints"
                }
              }
            }
          }
        }
      }
    },
    "/v1/destinations/{id}/validate": {
      "post": {
        "operationId": "validateDestination",
        "tags": [
          "destinations"
        ],
        "summary": "Validate S3 destination connectivity",
        "description": "Assumes the configured IAM role and attempts to write and delete a test object in S3 to verify permissions. On success, the destination status changes to `valid`. Throttled to one validation per 30 seconds per destination.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Destination ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Validation complete",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Destination"
                },
                "example": {
                  "id": "1f1a9a3d-3a2e-4b1a-9f6a-8f6d8a7e3c1b",
                  "name": "Analytics Data Lake",
                  "bucket": "acme-data-lake",
                  "prefix": "events/prod",
                  "region": "us-east-1",
                  "role_arn": "arn:aws:iam::123456789012:role/EdgeMqDelivery",
                  "external_id": "edgemq-abcdef0123456789",
                  "kms_key_arn": null,
                  "path_style": false,
                  "status": "valid",
                  "validated_at": "2026-01-15T10:35:00.000Z",
                  "last_error": null,
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "updated_at": "2026-01-15T10:35:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "description": "Validation throttled (max 1 per 30 seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/too_many_requests",
                  "title": "Too Many Requests",
                  "status": 429,
                  "error": "too_many_requests",
                  "detail": "validate throttled",
                  "retry": true
                }
              }
            }
          }
        }
      }
    },
    "/v1/views": {
      "post": {
        "operationId": "createView",
        "tags": [
          "views"
        ],
        "summary": "Create a view definition",
        "description": "Create a new schema view definition in `draft` status. A view defines how raw JSON payloads are transformed into typed, columnar Parquet output using JSONPath column mappings and optional row filters. Views must be published before they can be attached to endpoints.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateViewRequest"
              },
              "example": {
                "name": "user_events",
                "description": "Extract user event fields for analytics pipeline",
                "definition": {
                  "columns": [
                    {
                      "name": "user_id",
                      "type": "VARCHAR",
                      "path": "$.userId",
                      "required": true
                    },
                    {
                      "name": "event_type",
                      "type": "VARCHAR",
                      "path": "$.event"
                    },
                    {
                      "name": "timestamp",
                      "type": "TIMESTAMP",
                      "path": "$.ts"
                    },
                    {
                      "name": "amount",
                      "type": "DOUBLE",
                      "path": "$.payload.amount",
                      "cast_failures": "dlq"
                    }
                  ],
                  "filters": [
                    {
                      "condition": "event_type != 'heartbeat'",
                      "description": "Exclude keepalive events"
                    }
                  ]
                },
                "format": "yaml",
                "tags": [
                  "analytics",
                  "user-events"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "View created in draft status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/View"
                },
                "example": {
                  "id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "name": "user_events",
                  "version": 1,
                  "definition": {
                    "columns": [
                      {
                        "name": "user_id",
                        "type": "VARCHAR",
                        "path": "$.userId",
                        "required": true
                      },
                      {
                        "name": "event_type",
                        "type": "VARCHAR",
                        "path": "$.event"
                      },
                      {
                        "name": "timestamp",
                        "type": "TIMESTAMP",
                        "path": "$.ts"
                      },
                      {
                        "name": "amount",
                        "type": "DOUBLE",
                        "path": "$.payload.amount",
                        "cast_failures": "dlq"
                      }
                    ],
                    "filters": [
                      {
                        "condition": "event_type != 'heartbeat'",
                        "description": "Exclude keepalive events"
                      }
                    ]
                  },
                  "format": "yaml",
                  "status": "draft",
                  "description": "Extract user event fields for analytics pipeline",
                  "tags": [
                    "analytics",
                    "user-events"
                  ],
                  "created_by": "admin@example.com",
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "published_at": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "get": {
        "operationId": "listViews",
        "tags": [
          "views"
        ],
        "summary": "List view definitions",
        "description": "Returns a paginated list of view definitions. Filter by status, name, or tags. By default only published views are returned.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by view status. Use `all` to include every status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "published",
                "archived",
                "all"
              ],
              "default": "published"
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "Filter by exact view name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "description": "Filter by tag (can specify multiple).",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of items to return (1-200).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of items to skip for pagination.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of views",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewList"
                },
                "example": {
                  "items": [
                    {
                      "id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                      "name": "user_events",
                      "version": 1,
                      "definition": {
                        "columns": [
                          {
                            "name": "user_id",
                            "type": "VARCHAR",
                            "path": "$.userId",
                            "required": true
                          },
                          {
                            "name": "event_type",
                            "type": "VARCHAR",
                            "path": "$.event"
                          },
                          {
                            "name": "timestamp",
                            "type": "TIMESTAMP",
                            "path": "$.ts"
                          }
                        ]
                      },
                      "format": "yaml",
                      "status": "published",
                      "description": "Extract user event fields for analytics pipeline",
                      "tags": [
                        "analytics",
                        "user-events"
                      ],
                      "created_by": "admin@example.com",
                      "created_at": "2026-01-15T10:30:00.000Z",
                      "published_at": "2026-01-15T11:00:00.000Z",
                      "usage_count": 2
                    }
                  ],
                  "total": 1,
                  "limit": 50,
                  "offset": 0,
                  "next_offset": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/views/{id}": {
      "get": {
        "operationId": "getView",
        "tags": [
          "views"
        ],
        "summary": "Get a view definition",
        "description": "Retrieve the full details of a view definition including its version history and the list of endpoints it is attached to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "View details with version history and endpoint bindings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewDetail"
                },
                "example": {
                  "id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "name": "user_events",
                  "version": 1,
                  "definition": {
                    "columns": [
                      {
                        "name": "user_id",
                        "type": "VARCHAR",
                        "path": "$.userId",
                        "required": true
                      },
                      {
                        "name": "event_type",
                        "type": "VARCHAR",
                        "path": "$.event"
                      },
                      {
                        "name": "timestamp",
                        "type": "TIMESTAMP",
                        "path": "$.ts"
                      }
                    ],
                    "filters": [
                      {
                        "condition": "event_type != 'heartbeat'"
                      }
                    ]
                  },
                  "format": "yaml",
                  "status": "published",
                  "description": "Extract user event fields for analytics pipeline",
                  "tags": [
                    "analytics",
                    "user-events"
                  ],
                  "created_by": "admin@example.com",
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "published_at": "2026-01-15T11:00:00.000Z",
                  "versions": [
                    {
                      "id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                      "version": 1,
                      "status": "published",
                      "published_at": "2026-01-15T11:00:00.000Z",
                      "created_by": "admin@example.com"
                    }
                  ],
                  "endpoints": [
                    {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "region": "iad",
                      "binding_id": "d0e1f2a3-b4c5-6789-0123-ef0123456789",
                      "is_required": false,
                      "activated_at": "2026-01-16T09:00:00.000Z",
                      "canary_start_ts": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateView",
        "tags": [
          "views"
        ],
        "summary": "Update a draft view",
        "description": "Update the description, definition, or tags of a view that is still in `draft` status. Published and archived views cannot be updated; create a new version instead.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID (must be in draft status)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateViewRequest"
              },
              "example": {
                "description": "Updated description for user events view",
                "definition": {
                  "columns": [
                    {
                      "name": "user_id",
                      "type": "VARCHAR",
                      "path": "$.userId",
                      "required": true
                    },
                    {
                      "name": "event_type",
                      "type": "VARCHAR",
                      "path": "$.event"
                    },
                    {
                      "name": "timestamp",
                      "type": "TIMESTAMP",
                      "path": "$.ts"
                    },
                    {
                      "name": "session_id",
                      "type": "VARCHAR",
                      "path": "$.sessionId"
                    }
                  ]
                },
                "tags": [
                  "analytics",
                  "user-events",
                  "sessions"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "View updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/View"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "operationId": "deleteView",
        "tags": [
          "views"
        ],
        "summary": "Delete a draft view",
        "description": "Permanently delete a view definition that is in `draft` status. Published and archived views cannot be deleted; archive them instead.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID (must be in draft status)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "View deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "View is not in draft status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/validation_error",
                  "title": "Validation Error",
                  "status": 422,
                  "error": "validation_error",
                  "detail": "only draft views can be deleted"
                }
              }
            }
          }
        }
      }
    },
    "/v1/views/{id}/publish": {
      "post": {
        "operationId": "publishView",
        "tags": [
          "views"
        ],
        "summary": "Publish a view",
        "description": "Transition a view from `draft` to `published` status. Once published, the view definition becomes immutable and can be attached to endpoints. To make changes, create a new version.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID (must be in draft status)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "View published",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/View"
                },
                "example": {
                  "id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "name": "user_events",
                  "version": 1,
                  "definition": {
                    "columns": [
                      {
                        "name": "user_id",
                        "type": "VARCHAR",
                        "path": "$.userId",
                        "required": true
                      },
                      {
                        "name": "event_type",
                        "type": "VARCHAR",
                        "path": "$.event"
                      },
                      {
                        "name": "timestamp",
                        "type": "TIMESTAMP",
                        "path": "$.ts"
                      }
                    ]
                  },
                  "format": "yaml",
                  "status": "published",
                  "description": "Extract user event fields for analytics pipeline",
                  "tags": [
                    "analytics",
                    "user-events"
                  ],
                  "created_by": "admin@example.com",
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "published_at": "2026-01-15T11:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/views/{id}/versions": {
      "post": {
        "operationId": "createViewVersion",
        "tags": [
          "views"
        ],
        "summary": "Create a new version of a view",
        "description": "Create a new draft version of an existing view. The new version copies the definition from the specified source version (or the latest version by default). Use this to iterate on a published view without affecting live endpoints.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID of any existing version in the view lineage",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateViewVersionRequest"
              },
              "example": {
                "copy_from_version": 1,
                "description": "Add session_id column for attribution"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "New version created in draft status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/View"
                },
                "example": {
                  "id": "d0e1f2a3-b4c5-6789-0123-456789abcdef",
                  "name": "user_events",
                  "version": 2,
                  "definition": {
                    "columns": [
                      {
                        "name": "user_id",
                        "type": "VARCHAR",
                        "path": "$.userId",
                        "required": true
                      },
                      {
                        "name": "event_type",
                        "type": "VARCHAR",
                        "path": "$.event"
                      },
                      {
                        "name": "timestamp",
                        "type": "TIMESTAMP",
                        "path": "$.ts"
                      }
                    ]
                  },
                  "format": "yaml",
                  "status": "draft",
                  "description": "Add session_id column for attribution",
                  "tags": [
                    "analytics",
                    "user-events"
                  ],
                  "created_by": "admin@example.com",
                  "created_at": "2026-01-16T09:00:00.000Z",
                  "published_at": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/views/{id}/archive": {
      "post": {
        "operationId": "archiveView",
        "tags": [
          "views"
        ],
        "summary": "Archive a view",
        "description": "Transition a published view to `archived` status. Archived views are detached from all endpoints and can no longer be attached. This is a soft-delete for published views.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID (must be in published status)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "View archived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/View"
                },
                "example": {
                  "id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "name": "user_events",
                  "version": 1,
                  "definition": {
                    "columns": [
                      {
                        "name": "user_id",
                        "type": "VARCHAR",
                        "path": "$.userId",
                        "required": true
                      }
                    ]
                  },
                  "format": "yaml",
                  "status": "archived",
                  "description": "Extract user event fields for analytics pipeline",
                  "tags": [
                    "analytics"
                  ],
                  "created_by": "admin@example.com",
                  "created_at": "2026-01-15T10:30:00.000Z",
                  "published_at": "2026-01-15T11:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/views/preview": {
      "post": {
        "operationId": "previewView",
        "tags": [
          "views"
        ],
        "summary": "Preview a view against sample data",
        "description": "Test a view definition against user-provided sample JSON payloads without saving anything. Returns extracted columns, null rates, compiled SQL, and any warnings. Use this for real-time feedback during view development.\n\nResource limits: max 100 samples, 64KB per sample, 100 columns, 10 second timeout.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewViewRequest"
              },
              "example": {
                "definition": {
                  "columns": [
                    {
                      "name": "user_id",
                      "type": "VARCHAR",
                      "path": "$.userId"
                    },
                    {
                      "name": "event_type",
                      "type": "VARCHAR",
                      "path": "$.event"
                    },
                    {
                      "name": "timestamp",
                      "type": "TIMESTAMP",
                      "path": "$.ts"
                    }
                  ],
                  "filters": [
                    {
                      "condition": "event_type != 'heartbeat'"
                    }
                  ]
                },
                "samples": [
                  "{\"userId\": \"usr_abc123\", \"event\": \"page_view\", \"ts\": \"2026-01-15T10:30:00.000Z\"}",
                  "{\"userId\": \"usr_def456\", \"event\": \"heartbeat\", \"ts\": \"2026-01-15T10:30:01.000Z\"}"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preview results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreviewViewResponse"
                },
                "example": {
                  "success": true,
                  "rows": [
                    {
                      "user_id": "usr_abc123",
                      "event_type": "page_view",
                      "timestamp": "2026-01-15T10:30:00.000Z"
                    }
                  ],
                  "columns": [
                    {
                      "name": "user_id",
                      "type": "VARCHAR",
                      "nullCount": 0,
                      "totalCount": 1,
                      "nullRate": 0,
                      "sampleValues": [
                        "usr_abc123"
                      ]
                    },
                    {
                      "name": "event_type",
                      "type": "VARCHAR",
                      "nullCount": 0,
                      "totalCount": 1,
                      "nullRate": 0,
                      "sampleValues": [
                        "page_view"
                      ]
                    },
                    {
                      "name": "timestamp",
                      "type": "TIMESTAMP",
                      "nullCount": 0,
                      "totalCount": 1,
                      "nullRate": 0,
                      "sampleValues": [
                        "2026-01-15T10:30:00.000Z"
                      ]
                    }
                  ],
                  "rowCount": 1,
                  "compiledSql": "SELECT json_extract_string(raw, '$.userId') AS user_id, json_extract_string(raw, '$.event') AS event_type, CAST(json_extract_string(raw, '$.ts') AS TIMESTAMP) AS timestamp FROM source WHERE event_type != 'heartbeat'",
                  "warnings": [],
                  "errors": [],
                  "executionTimeMs": 12.4
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/endpoints/{endpoint_id}/views": {
      "post": {
        "operationId": "attachViewToEndpoint",
        "tags": [
          "bindings"
        ],
        "summary": "Attach a view to an endpoint",
        "description": "Bind a published view definition to an ingest endpoint. Once attached, the view's schema transformation is applied to every sealed segment on this endpoint. Optionally set `canary_start_ts` for gradual rollout.",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AttachViewRequest"
              },
              "example": {
                "view_id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                "is_required": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "View attached to endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewBinding"
                },
                "example": {
                  "id": "d0e1f2a3-b4c5-6789-0123-ef0123456789",
                  "endpoint_id": "550e8400-e29b-41d4-a716-446655440000",
                  "view_id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "view_name": "user_events",
                  "view_version": 1,
                  "is_required": false,
                  "activated_at": "2026-01-16T09:00:00.000Z",
                  "activated_by": "admin@example.com",
                  "deactivated_at": null,
                  "canary_start_ts": null,
                  "status": "active"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "View is already attached to this endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "https://api.edge.mq/errors/view_already_attached",
                  "title": "Conflict",
                  "status": 409,
                  "error": "view_already_attached",
                  "detail": "This view is already attached to this endpoint"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "get": {
        "operationId": "listEndpointViews",
        "tags": [
          "bindings"
        ],
        "summary": "List view bindings for an endpoint",
        "description": "Returns all view definitions attached to an endpoint, including both active and inactive (detached) bindings.",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of view bindings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewBindingList"
                },
                "example": {
                  "items": [
                    {
                      "id": "d0e1f2a3-b4c5-6789-0123-ef0123456789",
                      "endpoint_id": "550e8400-e29b-41d4-a716-446655440000",
                      "view_id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                      "view_name": "user_events",
                      "view_version": 1,
                      "is_required": false,
                      "activated_at": "2026-01-16T09:00:00.000Z",
                      "activated_by": "admin@example.com",
                      "deactivated_at": null,
                      "canary_start_ts": null,
                      "status": "active"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/endpoints/{endpoint_id}/views/{binding_id}": {
      "delete": {
        "operationId": "detachViewFromEndpoint",
        "tags": [
          "bindings"
        ],
        "summary": "Detach a view from an endpoint",
        "description": "Remove a view binding from an endpoint by soft-deleting it (setting `deactivated_at`). The view definition itself is not affected. Ingest nodes are notified via config epoch bump.",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "binding_id",
            "in": "path",
            "required": true,
            "description": "View binding ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "View detached successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/endpoints/{id}/commits": {
      "get": {
        "operationId": "listEndpointCommits",
        "tags": [
          "observability"
        ],
        "summary": "List sealed segments for an endpoint",
        "description": "Returns a cursor-paginated list of sealed segment commits for an endpoint. Each commit represents a WAL segment that has been uploaded to S3 with its output artifacts (raw segments, Parquet files, etc.). Use the `before` cursor for pagination.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of items to return (1-200).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "Cursor for pagination: return commits uploaded before this ISO 8601 timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of segment commits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitList"
                },
                "example": {
                  "items": [
                    {
                      "id": "10042",
                      "node_id": "550e8400-e29b-41d4-a716-446655440000",
                      "seq": 17,
                      "wal_bytes": 2097152,
                      "duration_ms": 340,
                      "uploaded_at": "2026-01-15T10:30:00.000Z",
                      "artifacts": {
                        "parquet": {
                          "key": "s3://acme-data-lake/events/prod/seg-17.parquet",
                          "bytes": 1048576
                        }
                      }
                    },
                    {
                      "id": "10041",
                      "node_id": "550e8400-e29b-41d4-a716-446655440000",
                      "seq": 16,
                      "wal_bytes": 1835008,
                      "duration_ms": 290,
                      "uploaded_at": "2026-01-15T10:25:00.000Z",
                      "artifacts": {
                        "parquet": {
                          "key": "s3://acme-data-lake/events/prod/seg-16.parquet",
                          "bytes": 921600
                        }
                      }
                    }
                  ],
                  "next_cursor": "2026-01-15T10:25:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/views/{id}/metrics": {
      "get": {
        "operationId": "getViewMetrics",
        "tags": [
          "observability"
        ],
        "summary": "Get data quality metrics for a view",
        "description": "Returns aggregated data quality metrics for a view across all endpoints it is bound to. Includes 24-hour rolling windows and lifetime totals for rows processed, accepted, dropped, and sent to DLQ. Metrics are updated every 5 minutes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "View metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewMetrics"
                },
                "example": {
                  "view_id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "view_name": "user_events",
                  "view_version": 1,
                  "endpoints": [
                    {
                      "endpoint_id": "550e8400-e29b-41d4-a716-446655440000",
                      "endpoint_name": "prod-ingest",
                      "last_24h": {
                        "processed": 15234,
                        "accepted": 14980,
                        "dropped": 212,
                        "dlq": 42
                      },
                      "all_time": {
                        "processed": 1048576,
                        "accepted": 1031200,
                        "dropped": 14876,
                        "dlq": 2500
                      },
                      "error_rate_pct": 1.67,
                      "has_errors": false,
                      "error_count_24h": 0,
                      "total_error_count": 0,
                      "last_error_at": null,
                      "last_error_code": null,
                      "last_error_message": null,
                      "last_updated_at": "2026-01-15T10:30:00.000Z"
                    }
                  ],
                  "aggregated": {
                    "last_24h": {
                      "processed": 15234,
                      "accepted": 14980,
                      "dropped": 212,
                      "dlq": 42
                    },
                    "all_time": {
                      "processed": 1048576,
                      "accepted": 1031200,
                      "dropped": 14876,
                      "dlq": 2500
                    },
                    "error_rate_pct": 1.67,
                    "has_errors": false,
                    "total_error_count_24h": 0
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/views/{id}/dlq-files": {
      "get": {
        "operationId": "listViewDlqFiles",
        "tags": [
          "observability"
        ],
        "summary": "List dead letter queue files for a view",
        "description": "Returns DLQ (dead letter queue) files for a view containing rows that failed validation or type casting. Each file includes a presigned S3 URL valid for 1 hour for downloading and inspecting rejected rows.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "View ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "DLQ files with presigned download URLs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DlqFileList"
                },
                "example": {
                  "view_id": "c9d0e1f2-a3b4-5678-9012-cdef01234567",
                  "view_name": "user_events",
                  "view_version": 1,
                  "files": [
                    {
                      "endpoint_id": "550e8400-e29b-41d4-a716-446655440000",
                      "endpoint_name": "prod-ingest",
                      "segment_seq": 17,
                      "s3_key": "dlq/user_events/seg-17.parquet",
                      "s3_bucket": "acme-data-lake",
                      "region": "us-east-1",
                      "bytes": 4096,
                      "rows": 42,
                      "created_at": "2026-01-15T10:30:00.000Z",
                      "download_url": "https://acme-data-lake.s3.amazonaws.com/dlq/user_events/seg-17.parquet?X-Amz-Signature=..."
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT obtained via POST /v1/token/exchange with an API key"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid authentication token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "https://api.edge.mq/errors/unauthorized",
              "title": "Unauthorized",
              "status": 401,
              "error": "unauthorized",
              "detail": "Missing or invalid bearer token."
            }
          }
        }
      },
      "Forbidden": {
        "description": "Insufficient permissions",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "https://api.edge.mq/errors/forbidden",
              "title": "Forbidden",
              "status": 403,
              "error": "forbidden",
              "detail": "Your token does not have the required scope for this operation."
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "https://api.edge.mq/errors/not_found",
              "title": "Not Found",
              "status": 404,
              "error": "not_found",
              "detail": "The requested resource does not exist."
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request validation failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "https://api.edge.mq/errors/validation_error",
              "title": "Validation Error",
              "status": 422,
              "error": "validation_error",
              "detail": "Request body failed validation."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "RFC 7807 Problem Details error response",
        "properties": {
          "type": {
            "type": "string",
            "description": "URI reference identifying the problem type",
            "example": "https://api.edge.mq/errors/validation_error"
          },
          "title": {
            "type": "string",
            "description": "Short human-readable summary of the problem",
            "example": "Validation Error"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code",
            "example": 422
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation specific to this occurrence",
            "example": "region must be a short code like lhr or iad"
          },
          "error": {
            "type": "string",
            "description": "Machine-readable error code",
            "example": "validation_error"
          },
          "retry": {
            "type": "boolean",
            "description": "Whether the client should retry the request"
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "error"
        ]
      },
      "TokenExchangeRequest": {
        "type": "object",
        "required": [
          "api_key"
        ],
        "additionalProperties": false,
        "properties": {
          "api_key": {
            "type": "string",
            "minLength": 8,
            "description": "Your EdgeMQ API key"
          },
          "scope": {
            "type": "string",
            "enum": [
              "ingest",
              "metrics",
              "admin"
            ],
            "default": "ingest",
            "description": "Permission scope for the issued token. Must be allowed by the API key."
          },
          "ttl": {
            "type": "string",
            "description": "Token lifetime as a duration string (e.g. '15m', '1h'). Defaults to 15 minutes.",
            "example": "15m"
          }
        }
      },
      "TokenExchangeResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in"
        ],
        "properties": {
          "access_token": {
            "type": "string",
            "description": "JWT access token to use in Authorization header"
          },
          "token_type": {
            "type": "string",
            "description": "Token type, always 'Bearer'",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Token lifetime in seconds",
            "example": 900
          }
        }
      },
      "Endpoint": {
        "type": "object",
        "required": [
          "id",
          "region",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique endpoint identifier"
          },
          "region": {
            "type": "string",
            "description": "Three-letter region code where the endpoint is deployed",
            "example": "iad"
          },
          "status": {
            "type": "string",
            "enum": [
              "deploying",
              "active",
              "degraded",
              "paused",
              "draining",
              "failed"
            ],
            "description": "Computed endpoint status based on node health, DNS, and certificate state"
          },
          "destination_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Bound S3 destination ID"
          },
          "destination_name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the bound S3 destination"
          },
          "destination_status": {
            "type": "string",
            "nullable": true,
            "description": "Validation status of the bound destination"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the endpoint was created"
          },
          "dns_ipv4": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IPv4 addresses assigned to the endpoint"
          },
          "dns_ipv6": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IPv6 addresses assigned to the endpoint"
          },
          "segment_bytes": {
            "type": "integer",
            "nullable": true,
            "description": "WAL segment size in bytes. When null, a plan-based default applies.",
            "example": 33554432
          },
          "segment_max_age_seconds": {
            "type": "integer",
            "nullable": true,
            "description": "Maximum segment age in seconds before time-based seal. Default 60s.",
            "example": 60
          },
          "output_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "segments",
                "parquet",
                "parquet_views"
              ]
            },
            "description": "Active output formats for this endpoint",
            "example": [
              "segments",
              "parquet"
            ]
          }
        }
      },
      "CreateEndpointRequest": {
        "type": "object",
        "required": [
          "region"
        ],
        "additionalProperties": false,
        "properties": {
          "region": {
            "type": "string",
            "pattern": "^[a-z]{3}$",
            "description": "Three-letter region code (e.g. iad, lhr, fra)",
            "example": "iad"
          },
          "destination_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional. UUID of an existing S3 destination to bind at creation time. Create a destination first via POST /v1/destinations, then pass its id here."
          },
          "segment_bytes": {
            "type": "integer",
            "minimum": 8388608,
            "maximum": 1073741824,
            "description": "WAL segment size in bytes (8MB - 1GB). Applies on next rotation."
          },
          "output_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "segments",
                "parquet",
                "parquet_views"
              ]
            },
            "minItems": 1,
            "default": [
              "segments"
            ],
            "description": "Output formats for this endpoint. At least one required."
          }
        }
      },
      "UpdateEndpointRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "destination_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "S3 destination to bind (or null to unbind)"
          },
          "segment_bytes": {
            "type": "integer",
            "minimum": 8388608,
            "maximum": 1073741824,
            "nullable": true,
            "description": "WAL segment size in bytes (8MB - 1GB). Null to reset to default."
          },
          "segment_max_age_seconds": {
            "type": "integer",
            "minimum": 10,
            "maximum": 3600,
            "nullable": true,
            "description": "Max segment age in seconds (10-3600). Null to reset to default 60s."
          },
          "output_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "segments",
                "parquet",
                "parquet_views"
              ]
            },
            "minItems": 1,
            "nullable": true,
            "description": "Output formats. At least one required if provided."
          }
        }
      },
      "EndpointList": {
        "type": "object",
        "required": [
          "items",
          "total",
          "limit",
          "offset",
          "next_offset"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Endpoint"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total number of matching endpoints"
          },
          "limit": {
            "type": "integer",
            "description": "Page size used"
          },
          "offset": {
            "type": "integer",
            "description": "Current offset"
          },
          "next_offset": {
            "type": "integer",
            "nullable": true,
            "description": "Offset for the next page, or null if this is the last page"
          }
        }
      },
      "Destination": {
        "type": "object",
        "required": [
          "id",
          "name",
          "bucket",
          "prefix",
          "region",
          "role_arn",
          "external_id",
          "path_style",
          "status",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique destination identifier"
          },
          "name": {
            "type": "string",
            "description": "Human-readable name for the destination",
            "example": "Analytics Data Lake"
          },
          "bucket": {
            "type": "string",
            "description": "S3 bucket name",
            "example": "acme-data-lake"
          },
          "prefix": {
            "type": "string",
            "description": "S3 key prefix for all delivered objects",
            "example": "events/prod"
          },
          "region": {
            "type": "string",
            "description": "AWS region of the S3 bucket",
            "example": "us-east-1"
          },
          "role_arn": {
            "type": "string",
            "description": "IAM role ARN that EdgeMQ assumes for S3 access",
            "example": "arn:aws:iam::123456789012:role/EdgeMqDelivery"
          },
          "external_id": {
            "type": "string",
            "description": "External ID used when assuming the IAM role (auto-generated)",
            "example": "edgemq-abcdef0123456789"
          },
          "kms_key_arn": {
            "type": "string",
            "nullable": true,
            "description": "KMS key ARN for SSE-KMS encryption. Null for SSE-S3."
          },
          "path_style": {
            "type": "boolean",
            "description": "Use path-style S3 URLs instead of virtual-hosted-style",
            "default": false
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "valid",
              "invalid"
            ],
            "description": "Validation status. Starts as 'pending', changes to 'valid' or 'invalid' after validation."
          },
          "validated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the destination was last validated"
          },
          "last_error": {
            "type": "string",
            "nullable": true,
            "description": "Error message from the last failed validation"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the destination was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the destination was last updated"
          }
        }
      },
      "DestinationWithRefCount": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Destination"
          },
          {
            "type": "object",
            "required": [
              "ref_count"
            ],
            "properties": {
              "ref_count": {
                "type": "integer",
                "description": "Number of endpoints referencing this destination"
              }
            }
          }
        ]
      },
      "CreateDestinationRequest": {
        "type": "object",
        "required": [
          "name",
          "bucket",
          "region",
          "role_arn"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Human-readable name (must be unique within your account)"
          },
          "bucket": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$",
            "description": "S3 bucket name"
          },
          "prefix": {
            "type": "string",
            "default": "",
            "description": "S3 key prefix. Leading/trailing slashes are normalized."
          },
          "region": {
            "type": "string",
            "description": "AWS region of the S3 bucket",
            "example": "us-east-1"
          },
          "role_arn": {
            "type": "string",
            "pattern": "^arn:aws:iam::\\d{12}:role/[A-Za-z0-9+=,.@\\-_/]+$",
            "description": "IAM role ARN that EdgeMQ will assume"
          },
          "kms_key_arn": {
            "type": "string",
            "pattern": "^arn:aws:kms:[a-z0-9-]+:\\d{12}:key/[0-9a-fA-F\\-]+$",
            "description": "KMS key ARN for SSE-KMS encryption. Omit for SSE-S3."
          },
          "path_style": {
            "type": "boolean",
            "default": false,
            "description": "Use path-style S3 URLs"
          }
        }
      },
      "UpdateDestinationRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "New name"
          },
          "bucket": {
            "type": "string",
            "description": "New S3 bucket name (triggers re-validation)"
          },
          "prefix": {
            "type": "string",
            "description": "New S3 key prefix (triggers re-validation)"
          },
          "region": {
            "type": "string",
            "description": "New AWS region (triggers re-validation)"
          },
          "role_arn": {
            "type": "string",
            "description": "New IAM role ARN (triggers re-validation)"
          },
          "kms_key_arn": {
            "type": "string",
            "nullable": true,
            "description": "New KMS key ARN or null to disable SSE-KMS (triggers re-validation)"
          },
          "path_style": {
            "type": "boolean",
            "description": "Use path-style S3 URLs (triggers re-validation)"
          }
        }
      },
      "DestinationList": {
        "type": "object",
        "required": [
          "items",
          "total",
          "limit",
          "offset",
          "next_offset"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DestinationWithRefCount"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total number of destinations"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "next_offset": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "ColumnDefinition": {
        "type": "object",
        "required": [
          "name",
          "type",
          "path"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Output column name"
          },
          "type": {
            "type": "string",
            "enum": [
              "VARCHAR",
              "BIGINT",
              "DOUBLE",
              "BOOLEAN",
              "TIMESTAMP",
              "DATE",
              "JSON",
              "INTEGER",
              "FLOAT",
              "TEXT",
              "BLOB",
              "DECIMAL"
            ],
            "description": "DuckDB column type for the output Parquet file"
          },
          "path": {
            "type": "string",
            "minLength": 1,
            "description": "JSONPath expression to extract the value (e.g. '$.userId')"
          },
          "required": {
            "type": "boolean",
            "default": false,
            "description": "If true, rows with null values for this column are rejected"
          },
          "cast_failures": {
            "type": "string",
            "enum": [
              "null",
              "drop_row",
              "dlq"
            ],
            "description": "What to do when type casting fails: null (set to null), drop_row (exclude row), dlq (send row to dead letter queue)"
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "description": "Human-readable column description"
          },
          "max_length": {
            "type": "integer",
            "minimum": 1,
            "description": "Maximum string length (VARCHAR columns only)"
          },
          "default": {
            "type": "string",
            "description": "Default value when extraction returns null"
          }
        }
      },
      "FilterDefinition": {
        "type": "object",
        "required": [
          "condition"
        ],
        "properties": {
          "condition": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000,
            "description": "SQL WHERE clause condition applied after column extraction"
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "description": "Human-readable filter description"
          }
        }
      },
      "ViewDefinitionContent": {
        "type": "object",
        "required": [
          "columns"
        ],
        "properties": {
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ColumnDefinition"
            },
            "minItems": 1,
            "maxItems": 1000,
            "description": "Column extraction definitions"
          },
          "filters": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilterDefinition"
            },
            "description": "Optional row filters applied after extraction"
          }
        }
      },
      "View": {
        "type": "object",
        "required": [
          "id",
          "name",
          "version",
          "definition",
          "format",
          "status",
          "created_by",
          "created_at",
          "tags"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique view identifier"
          },
          "name": {
            "type": "string",
            "description": "View name (lowercase alphanumeric with underscores)",
            "example": "user_events"
          },
          "version": {
            "type": "integer",
            "description": "Version number (auto-incremented)",
            "example": 1
          },
          "definition": {
            "$ref": "#/components/schemas/ViewDefinitionContent"
          },
          "format": {
            "type": "string",
            "enum": [
              "yaml",
              "json"
            ],
            "description": "Storage format for the definition"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published",
              "archived"
            ],
            "description": "View lifecycle status"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable description"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags for filtering and organization"
          },
          "created_by": {
            "type": "string",
            "description": "Email of the user who created this version"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this version was created"
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When this version was published (null for drafts)"
          }
        }
      },
      "ViewWithUsageCount": {
        "allOf": [
          {
            "$ref": "#/components/schemas/View"
          },
          {
            "type": "object",
            "properties": {
              "usage_count": {
                "type": "integer",
                "description": "Number of endpoints using this view"
              }
            }
          }
        ]
      },
      "ViewDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/View"
          },
          {
            "type": "object",
            "properties": {
              "versions": {
                "type": "array",
                "description": "All versions in this view's lineage",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "version",
                    "status",
                    "created_by"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "published_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "created_by": {
                      "type": "string"
                    }
                  }
                }
              },
              "endpoints": {
                "type": "array",
                "description": "Endpoints this view is currently attached to",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "region",
                    "binding_id",
                    "is_required",
                    "activated_at"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "region": {
                      "type": "string"
                    },
                    "binding_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "is_required": {
                      "type": "boolean"
                    },
                    "activated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "canary_start_ts": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "CreateViewRequest": {
        "type": "object",
        "required": [
          "name",
          "definition"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]*$",
            "minLength": 1,
            "maxLength": 64,
            "description": "View name (lowercase alphanumeric with underscores, must start with a letter)"
          },
          "description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Human-readable description"
          },
          "definition": {
            "$ref": "#/components/schemas/ViewDefinitionContent"
          },
          "format": {
            "type": "string",
            "enum": [
              "yaml",
              "json"
            ],
            "default": "yaml",
            "description": "Storage format for the definition"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 32
            },
            "maxItems": 10,
            "default": [],
            "description": "Tags for filtering and organization"
          }
        }
      },
      "UpdateViewRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Updated description"
          },
          "definition": {
            "$ref": "#/components/schemas/ViewDefinitionContent"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 32
            },
            "maxItems": 10,
            "description": "Updated tags"
          }
        }
      },
      "CreateViewVersionRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "copy_from_version": {
            "type": "integer",
            "minimum": 1,
            "description": "Version number to copy the definition from. Defaults to the latest version."
          },
          "description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Description for the new version"
          }
        }
      },
      "ViewList": {
        "type": "object",
        "required": [
          "items",
          "total",
          "limit",
          "offset",
          "next_offset"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ViewWithUsageCount"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "next_offset": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "PreviewViewRequest": {
        "type": "object",
        "required": [
          "definition",
          "samples"
        ],
        "additionalProperties": false,
        "properties": {
          "definition": {
            "$ref": "#/components/schemas/ViewDefinitionContent"
          },
          "samples": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "Array of raw JSON payload strings to test against (max 64KB each)"
          }
        }
      },
      "PreviewColumnStats": {
        "type": "object",
        "required": [
          "name",
          "type",
          "nullCount",
          "totalCount",
          "nullRate"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "nullCount": {
            "type": "integer"
          },
          "totalCount": {
            "type": "integer"
          },
          "nullRate": {
            "type": "number"
          },
          "sampleValues": {
            "type": "array",
            "items": {}
          }
        }
      },
      "PreviewViewResponse": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the preview executed without fatal errors"
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Extracted rows after applying column mappings and filters"
          },
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PreviewColumnStats"
            },
            "description": "Per-column statistics including null rates"
          },
          "rowCount": {
            "type": "integer",
            "description": "Number of rows in the output"
          },
          "compiledSql": {
            "type": "string",
            "description": "The DuckDB SQL generated from the view definition"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Non-fatal warnings from compilation or execution"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "type",
                "message"
              ],
              "properties": {
                "type": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "rowIndex": {
                  "type": "integer"
                },
                "column": {
                  "type": "string"
                }
              }
            },
            "description": "Errors encountered during preview"
          },
          "executionTimeMs": {
            "type": "number",
            "description": "Execution time in milliseconds"
          }
        }
      },
      "AttachViewRequest": {
        "type": "object",
        "required": [
          "view_id"
        ],
        "additionalProperties": false,
        "properties": {
          "view_id": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the published view to attach"
          },
          "is_required": {
            "type": "boolean",
            "default": false,
            "description": "If true, ingest fails when view extraction fails. If false, view output is optional."
          },
          "canary_start_ts": {
            "type": "string",
            "format": "date-time",
            "description": "Apply view only to segments sealed after this timestamp (for gradual rollout)"
          }
        }
      },
      "ViewBinding": {
        "type": "object",
        "required": [
          "id",
          "endpoint_id",
          "view_id",
          "view_name",
          "view_version",
          "is_required",
          "activated_at",
          "activated_by",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique binding identifier"
          },
          "endpoint_id": {
            "type": "string",
            "format": "uuid"
          },
          "view_id": {
            "type": "string",
            "format": "uuid"
          },
          "view_name": {
            "type": "string",
            "description": "Name of the attached view"
          },
          "view_version": {
            "type": "integer",
            "description": "Version of the attached view"
          },
          "is_required": {
            "type": "boolean",
            "description": "Whether view output is required for successful ingest"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the binding was activated"
          },
          "activated_by": {
            "type": "string",
            "description": "Who activated the binding"
          },
          "deactivated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the binding was deactivated (null if active)"
          },
          "canary_start_ts": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Canary start timestamp for gradual rollout"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "description": "Binding status"
          }
        }
      },
      "ViewBindingList": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ViewBinding"
            }
          }
        }
      },
      "SegmentCommit": {
        "type": "object",
        "required": [
          "id",
          "node_id",
          "seq",
          "wal_bytes",
          "uploaded_at",
          "artifacts"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Commit sequence ID"
          },
          "node_id": {
            "type": "string",
            "format": "uuid",
            "description": "Ingest node that produced the segment"
          },
          "seq": {
            "type": "integer",
            "description": "Segment sequence number on the node"
          },
          "wal_bytes": {
            "type": "integer",
            "description": "Raw WAL segment size in bytes"
          },
          "duration_ms": {
            "type": "integer",
            "nullable": true,
            "description": "Upload duration in milliseconds"
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the segment was uploaded to S3"
          },
          "artifacts": {
            "type": "object",
            "additionalProperties": true,
            "description": "Output artifacts manifest (keys, sizes) keyed by format (e.g. parquet, segments)"
          }
        }
      },
      "CommitList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SegmentCommit"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "ISO 8601 timestamp cursor for the next page. Null if no more results."
          }
        }
      },
      "MetricsBucket": {
        "type": "object",
        "required": [
          "processed",
          "accepted",
          "dropped",
          "dlq"
        ],
        "properties": {
          "processed": {
            "type": "integer",
            "description": "Total rows attempted"
          },
          "accepted": {
            "type": "integer",
            "description": "Successfully processed rows"
          },
          "dropped": {
            "type": "integer",
            "description": "Rows filtered by WHERE clause"
          },
          "dlq": {
            "type": "integer",
            "description": "Rows rejected to dead letter queue"
          }
        }
      },
      "ViewMetrics": {
        "type": "object",
        "required": [
          "view_id",
          "view_name",
          "view_version",
          "endpoints",
          "aggregated"
        ],
        "properties": {
          "view_id": {
            "type": "string",
            "format": "uuid"
          },
          "view_name": {
            "type": "string"
          },
          "view_version": {
            "type": "integer"
          },
          "endpoints": {
            "type": "array",
            "description": "Per-endpoint metrics breakdown",
            "items": {
              "type": "object",
              "required": [
                "endpoint_id",
                "endpoint_name",
                "last_24h",
                "all_time",
                "error_rate_pct",
                "has_errors",
                "error_count_24h",
                "last_updated_at"
              ],
              "properties": {
                "endpoint_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "endpoint_name": {
                  "type": "string"
                },
                "last_24h": {
                  "$ref": "#/components/schemas/MetricsBucket"
                },
                "all_time": {
                  "$ref": "#/components/schemas/MetricsBucket"
                },
                "error_rate_pct": {
                  "type": "number",
                  "description": "Error rate: (dropped + dlq) / processed * 100"
                },
                "has_errors": {
                  "type": "boolean",
                  "description": "Whether the view has render errors in last 24h"
                },
                "error_count_24h": {
                  "type": "integer",
                  "description": "Number of render failures in last 24h"
                },
                "total_error_count": {
                  "type": "integer",
                  "description": "Total render failures (lifetime)"
                },
                "last_error_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "last_error_code": {
                  "type": "string",
                  "nullable": true
                },
                "last_error_message": {
                  "type": "string",
                  "nullable": true
                },
                "last_updated_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "aggregated": {
            "type": "object",
            "required": [
              "last_24h",
              "all_time",
              "error_rate_pct",
              "has_errors",
              "total_error_count_24h"
            ],
            "properties": {
              "last_24h": {
                "$ref": "#/components/schemas/MetricsBucket"
              },
              "all_time": {
                "$ref": "#/components/schemas/MetricsBucket"
              },
              "error_rate_pct": {
                "type": "number"
              },
              "has_errors": {
                "type": "boolean"
              },
              "total_error_count_24h": {
                "type": "integer"
              }
            }
          }
        }
      },
      "DlqFile": {
        "type": "object",
        "required": [
          "endpoint_id",
          "endpoint_name",
          "segment_seq",
          "s3_key",
          "s3_bucket",
          "region",
          "created_at",
          "download_url"
        ],
        "properties": {
          "endpoint_id": {
            "type": "string",
            "format": "uuid"
          },
          "endpoint_name": {
            "type": "string",
            "description": "Endpoint name"
          },
          "segment_seq": {
            "type": "integer",
            "description": "Segment sequence number"
          },
          "s3_key": {
            "type": "string",
            "description": "S3 object key for the DLQ file"
          },
          "s3_bucket": {
            "type": "string",
            "description": "S3 bucket name"
          },
          "region": {
            "type": "string",
            "description": "AWS region"
          },
          "bytes": {
            "type": "integer",
            "description": "File size in bytes"
          },
          "rows": {
            "type": "integer",
            "description": "Number of rejected rows in the file"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the DLQ file was created"
          },
          "download_url": {
            "type": "string",
            "description": "Presigned S3 URL for downloading (expires in 1 hour)"
          }
        }
      },
      "DlqFileList": {
        "type": "object",
        "required": [
          "view_id",
          "view_name",
          "view_version",
          "files",
          "total"
        ],
        "properties": {
          "view_id": {
            "type": "string",
            "format": "uuid"
          },
          "view_name": {
            "type": "string"
          },
          "view_version": {
            "type": "integer"
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DlqFile"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total number of DLQ files"
          }
        }
      }
    }
  }
}