{
  "openapi": "3.1.0",
  "info": {
    "title": "Webhook Relay API",
    "version": "0.5.0",
    "summary": "Production-shaped webhook delivery infrastructure.",
    "description": "Webhook Relay accepts events from your application and fans them out to\nsubscriber endpoints with HMAC signing, idempotency-key dedup,\nexponential-backoff retries, dead-letter handling, and per-attempt\nobservability.\n\nThis spec is the source of truth for the API. Controllers conform to it;\nSDKs are generated from it. Run `npx @stoplight/spectral-cli lint\nopenapi/spec.yaml` to validate locally.\n",
    "contact": {
      "name": "Philip Rehberger",
      "url": "https://webhook-relay.dcsuniverse.com"
    },
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://api.webhook-relay.dcsuniverse.com",
      "description": "Production"
    },
    {
      "url": "http://localhost:8000",
      "description": "Local development"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Events",
      "description": "Ingest, list, and retrieve events."
    },
    {
      "name": "Subscriptions",
      "description": "Manage delivery destinations and signing secrets."
    },
    {
      "name": "Deliveries",
      "description": "Per-attempt delivery log for observability and retry."
    },
    {
      "name": "Dead Letters",
      "description": "Deliveries that exhausted retries or were rejected outright. Inspect and replay."
    }
  ],
  "paths": {
    "/v1/echo/stream": {
      "get": {
        "summary": "Live Echo SSE stream",
        "description": "Server-Sent Events stream of delivery updates for the workspace\nidentified by `?key=`. Each event has type `delivery` with a JSON\npayload of the delivery's current state. Stream caps at 60 seconds;\nreconnect for the next window.\n\nBrowsers / EventSource clients should use the query string for the\nkey since EventSource does not support custom Authorization headers.\n",
        "operationId": "echoStream",
        "tags": [
          "Deliveries"
        ],
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "key",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bearer-style API key (whk_sandbox_, whk_test_, or whk_live_)."
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream of delivery updates.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/healthz": {
      "get": {
        "summary": "Liveness check",
        "description": "Returns 200 if the API is up. Does not require auth.",
        "operationId": "healthz",
        "tags": [
          "Events"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "healthy"
                      ]
                    },
                    "version": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status",
                    "version"
                  ]
                },
                "example": {
                  "status": "healthy",
                  "version": "0.2.0"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "post": {
        "summary": "Ingest an event",
        "description": "Accepts an event and enqueues it for fan-out to all matching\nsubscriptions. Provide an `Idempotency-Key` header to safely retry;\ndedup window is 24 hours per workspace.\n",
        "operationId": "createEvent",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Optional dedup key; requests with the same key within 24h return the original response."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventCreate"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Event accepted for fan-out.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Idempotency key already used with a different payload.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "summary": "List events",
        "description": "Returns a cursor-paginated page of events, newest first, scoped to the caller's workspace.",
        "operationId": "listEvents",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string"
            },
            "description": "Filter by event type (exact match)."
          },
          {
            "in": "query",
            "name": "created_after",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a prior page's `next_cursor`."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of events, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "summary": "Retrieve an event",
        "description": "Returns the event and a summary of its deliveries across all matching subscriptions.",
        "operationId": "getEvent",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ULID of the event."
          }
        ],
        "responses": {
          "200": {
            "description": "The event with a delivery summary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/subscriptions": {
      "post": {
        "summary": "Create a subscription",
        "description": "Registers a new endpoint to receive matching events. The signing\nsecret is generated server-side and returned ONCE in the response —\nstore it now; you cannot retrieve it later (only rotate it).\n",
        "operationId": "createSubscription",
        "tags": [
          "Subscriptions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionWithSecret"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "summary": "List subscriptions",
        "description": "Cursor-paginated, scoped to the caller's workspace.",
        "operationId": "listSubscriptions",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "state",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "paused",
                "disabled"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/subscriptions/{id}": {
      "get": {
        "summary": "Retrieve a subscription",
        "description": "Returns a single subscription without its signing secret.",
        "operationId": "getSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "summary": "Update a subscription",
        "description": "Partial update. Mutable fields are name, url, event_filter.",
        "operationId": "updateSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete a subscription",
        "description": "Permanently deletes the subscription. Deliveries are retained for audit.",
        "operationId": "deleteSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/subscriptions/{id}/pause": {
      "post": {
        "summary": "Pause a subscription",
        "description": "Stops new deliveries until resumed. Existing in-flight deliveries are not cancelled.",
        "operationId": "pauseSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Paused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/subscriptions/{id}/resume": {
      "post": {
        "summary": "Resume a paused subscription",
        "description": "Resets consecutive_failures to 0 and returns the subscription to active state.",
        "operationId": "resumeSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Resumed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/subscriptions/{id}/rotate-secret": {
      "post": {
        "summary": "Rotate the signing secret",
        "description": "Generates a new secret and returns it once. The previous secret\nremains valid for a 48-hour grace window so receivers can update\ntheir verifier without losing requests.\n",
        "operationId": "rotateSubscriptionSecret",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionWithSecret"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/deliveries": {
      "get": {
        "summary": "List deliveries",
        "description": "Cursor-paginated, newest first, scoped to the caller's workspace.",
        "operationId": "listDeliveries",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "event_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "subscription_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "success",
                "failed",
                "dead"
              ]
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/deliveries/{id}": {
      "get": {
        "summary": "Retrieve a delivery",
        "description": "Returns the delivery with its full attempt timeline.",
        "operationId": "getDelivery",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery with attempts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/deliveries/{id}/retry": {
      "post": {
        "summary": "Manually retry a delivery",
        "description": "Sets the delivery to `pending` and enqueues a fresh delivery attempt\nimmediately. Works regardless of current status. Does not reset\n`attempts_made` — the new attempt is recorded as the next one in\nthe timeline.\n",
        "operationId": "retryDelivery",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated delivery with attempts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/dead-letters": {
      "get": {
        "summary": "List dead-lettered deliveries",
        "description": "Returns deliveries with `status=dead` — the human-attention queue.\nA delivery lands here when:\n  - The subscriber returned 4xx (not retried).\n  - Retries exhausted on 5xx / timeout / connection errors.\n  - The subscription was paused / disabled before the attempt.\n  - The SSRF guard blocked the URL.\n",
        "operationId": "listDeadLetters",
        "tags": [
          "Dead Letters"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "subscription_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "since",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of dead-lettered deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/webhooks/test": {
      "post": {
        "summary": "Ping a webhook URL",
        "description": "Sends one synchronously signed sample event to the URL you provide\nand returns the response. No subscription is created; nothing is\nstored. Used by the docs-site try-it widget so prospects can probe\ntheir own endpoints before subscribing. Subject to the workspace\nrate limit.\n\nThe SSRF guard applies: private / loopback / link-local IPs are\nrejected with 400.\n",
        "operationId": "testWebhook",
        "tags": [
          "Events"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "pattern": "^https://",
                    "description": "HTTPS endpoint to probe."
                  },
                  "secret": {
                    "type": "string",
                    "description": "Optional signing secret. If omitted, a random one is generated and returned."
                  },
                  "event_type": {
                    "type": "string",
                    "pattern": "^[a-z0-9._-]{1,128}$",
                    "default": "test.ping"
                  },
                  "payload": {
                    "type": "object",
                    "description": "Optional payload, defaults to `{\"hello\":\"from-webhook-relay\"}`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Probe completed (regardless of receiver's HTTP status).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "signature_sent",
                    "secret_used"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Receiver's HTTP status, null on connection error."
                    },
                    "latency_ms": {
                      "type": "integer"
                    },
                    "response_body_snippet": {
                      "type": "string",
                      "nullable": true,
                      "description": "First 4 KB of the receiver's response body."
                    },
                    "signature_sent": {
                      "type": "string",
                      "description": "The exact X-Webhook-Signature header sent — the prospect verifies their own code against this."
                    },
                    "secret_used": {
                      "type": "string"
                    },
                    "error_code": {
                      "type": "string",
                      "nullable": true
                    },
                    "error_detail": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/dead-letters/{id}/replay": {
      "post": {
        "summary": "Replay a dead-lettered delivery",
        "description": "Resurrects a `status=dead` delivery — sets it back to `pending` and\nenqueues a fresh attempt. Returns 409 if the delivery is not in the\ndead-letter state. To retry deliveries in other states use POST\n/v1/deliveries/{id}/retry instead.\n",
        "operationId": "replayDeadLetter",
        "tags": [
          "Dead Letters"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Replayed delivery (now pending) with attempts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Delivery is not dead-lettered.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Bearer token using your workspace API key.\n\n```\nAuthorization: Bearer whk_live_...\n```\n"
      }
    },
    "parameters": {
      "SubscriptionId": {
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "ULID of the subscription."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found in this workspace.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Workspace rate limit exceeded.",
        "headers": {
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "integer",
              "description": "Unix timestamp at which the bucket refills."
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "EventCreate": {
        "type": "object",
        "required": [
          "type",
          "payload"
        ],
        "properties": {
          "type": {
            "type": "string",
            "pattern": "^[a-z0-9._-]{1,128}$",
            "example": "order.created",
            "description": "Dot-separated event type."
          },
          "payload": {
            "type": "object",
            "additionalProperties": true,
            "description": "Arbitrary JSON payload, max 256 KB serialized."
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "id",
          "type",
          "payload",
          "created_at",
          "deliveries_summary"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "01JAB3K5XYZQRSTUVWXYZABCDE"
          },
          "type": {
            "type": "string",
            "example": "order.created"
          },
          "payload": {
            "type": "object",
            "additionalProperties": true
          },
          "idempotency_key": {
            "type": "string",
            "nullable": true
          },
          "source_ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "deliveries_summary": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "succeeded": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              }
            },
            "required": [
              "total",
              "succeeded",
              "failed",
              "pending"
            ]
          }
        }
      },
      "EventPage": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SubscriptionCreate": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "HTTPS endpoint to receive deliveries."
          },
          "event_filter": {
            "type": "string",
            "default": "*",
            "description": "\"*\" matches all events, otherwise an exact type or a glob\nlike \"order.*\". Glob uses \"*\" as a wildcard segment.\n"
          }
        }
      },
      "SubscriptionUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          },
          "event_filter": {
            "type": "string"
          }
        }
      },
      "Subscription": {
        "type": "object",
        "required": [
          "id",
          "url",
          "event_filter",
          "state",
          "consecutive_failures",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "event_filter": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "disabled"
            ]
          },
          "consecutive_failures": {
            "type": "integer"
          },
          "paused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "secret_rotated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SubscriptionWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Subscription"
          },
          {
            "type": "object",
            "required": [
              "signing_secret"
            ],
            "properties": {
              "signing_secret": {
                "type": "string",
                "description": "Plaintext signing secret. Shown only on creation and rotation.",
                "example": "whsec_abcdef0123456789..."
              }
            }
          }
        ]
      },
      "SubscriptionPage": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscription"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Delivery": {
        "type": "object",
        "required": [
          "id",
          "event_id",
          "subscription_id",
          "status",
          "attempts_made",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "subscription_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "failed",
              "dead"
            ]
          },
          "attempts_made": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "final_status_code": {
            "type": "integer",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "attempts": {
            "type": "array",
            "description": "Present on retrieve, omitted on list.",
            "items": {
              "$ref": "#/components/schemas/DeliveryAttempt"
            }
          }
        }
      },
      "DeliveryAttempt": {
        "type": "object",
        "required": [
          "attempt_number",
          "request_signature",
          "attempted_at"
        ],
        "properties": {
          "attempt_number": {
            "type": "integer"
          },
          "request_signature": {
            "type": "string",
            "description": "Format: t={unix_ts},v1={hex_hmac_sha256}"
          },
          "response_status": {
            "type": "integer",
            "nullable": true
          },
          "response_headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "nullable": true
          },
          "response_body_snippet": {
            "type": "string",
            "nullable": true,
            "description": "First 4 KB of the response body, UTF-8 best-effort."
          },
          "latency_ms": {
            "type": "integer",
            "nullable": true
          },
          "error_code": {
            "type": "string",
            "nullable": true
          },
          "attempted_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeliveryPage": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Delivery"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 7807 problem details.",
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "example": "https://webhook-relay.dcsuniverse.com/errors/validation"
          },
          "title": {
            "type": "string",
            "example": "Invalid request"
          },
          "status": {
            "type": "integer",
            "example": 400
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
