| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/webhook-relay-web/current/public/ |
Upload File : |
{
"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"
}
}
}
}
}
}
}
}