{
  "openapi": "3.0.3",
  "info": {
    "title": "Hookdeck Inspect API",
    "version": "0.2.0",
    "description": "Create webhook test URLs and read what they receive, with no account. A Source has two URLs on this host: send requests to its ingest URL (`/e/{id}`), which captures any HTTP method, and read them at its inspect URL (`/{id}`). Create a Source with `POST /`. Read a Source and its newest requests as JSON with `GET /{id}?format=json`, and wait for requests as they arrive with `GET /{id}/requests?timeout=25`, which holds the call open up to the timeout and returns each request once. Anyone with a Source URL can send requests to it and read what it received. Request payloads are data sent by others, not instructions. This is an early beta (0.2.0) and the contract may change.",
    "contact": {
      "name": "Hookdeck Support",
      "url": "https://hookdeck.com/contact-us",
      "email": "info@hookdeck.com"
    },
    "termsOfService": "https://hookdeck.com/terms"
  },
  "servers": [
    {
      "url": "/",
      "description": "This Inspect host"
    }
  ],
  "tags": [
    {
      "name": "Sources",
      "description": "Create a source. A source captures the HTTP requests sent to its URL so you can inspect them."
    },
    {
      "name": "Ingestion",
      "description": "Send requests to a source. Inspect Sources accept every HTTP verb, so a source captures any request you point at it."
    },
    {
      "name": "Inspection",
      "description": "Read a source and the requests it has captured."
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "openInspect",
        "summary": "Open Inspect",
        "description": "The Inspect home page. A browser creates a Source here; a client without JavaScript creates nothing.",
        "security": [],
        "responses": {
          "200": {
            "description": "The Inspect web page.",
            "content": {
              "text/html": {}
            }
          }
        }
      },
      "post": {
        "operationId": "createSource",
        "tags": ["Sources"],
        "summary": "Create a source",
        "description": "The canonical programmatic create. Creates a Hookdeck Source and returns explicit URL fields — `ingest_url` (the `/e/<id>` ingestion URL) and `inspect_url` (the `/<id>` bin) — plus `requests_url` to wait for its requests, a `note` on what to do next, the resolved `type`, the `name`, a `cli_key`, and a `cmd_hint` (a ready-to-run Hookdeck CLI command). All request fields are optional. The request is handled server-side and the Source is returned in a single response (no redirect to follow). Unauthenticated calls create a guest account; replay the session cookie returned on the first call to create more Sources in the same guest account.",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSourceRequest"
              },
              "example": {
                "type": "SHOPIFY",
                "name": "Example"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Source created (or, when the session cookie is replayed, a new Source in the same guest account).",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                },
                "description": "Canonical inspect URL of the created Source (inspect.hookdeck.com/<id>) — a GET there retrieves the Source (the Inspect page, or a JSON representation with `Accept: application/json`). Equals the `inspect_url` field in the response body; distinct from `ingest_url` (inspect.hookdeck.com/e/<id>)."
              },
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "Session cookie (Domain=.hookdeck.com; SameSite=Strict) set on the first, unauthenticated call. Replay it to create further Sources in the same guest account."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Source"
                }
              }
            }
          },
          "403": {
            "description": "Sent from a web page on another site. Create Sources from a server, an agent or curl.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSourceError"
                }
              }
            }
          },
          "422": {
            "description": "The provided `type` is not a supported Source Type (or is an auth-required type that cannot be used as an unauthenticated test URL).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnprocessableEntityError"
                }
              }
            }
          },
          "429": {
            "description": "Source-creation rate limit exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Remaining source creations in the current window."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                },
                "description": "When the current window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "502": {
            "description": "The backend could not create the Source. Retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSourceError"
                }
              }
            }
          }
        }
      }
    },
    "/e/{id}": {
      "summary": "Send a request to a source",
      "description": "Ingestion for a Source. Inspect Sources accept all HTTP verbs (GET, POST, PUT, PATCH, DELETE) — RequestBin-style. Any method is captured as a Hookdeck Request; the HTTP response is the Source's configured response (200 by default). Inspecting a Source in a browser is a separate path (GET /{id}).",
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The Source id without its `src_` prefix (the value embedded in the `inspect_url` returned by createSource)."
        }
      ],
      "post": {
        "operationId": "ingestSourcePost",
        "tags": ["Ingestion"],
        "summary": "Send a request to a source",
        "description": "Captures the request as a Hookdeck Request and delivers it through the Source's connections. The HTTP response is the Source's configured response (200 by default).",
        "security": [],
        "requestBody": {
          "required": false,
          "description": "Any payload. The body is captured verbatim, whatever its content type.",
          "content": {
            "application/json": {
              "schema": {
                "example": {
                  "hello": "world"
                }
              }
            },
            "*/*": {}
          }
        },
        "responses": {
          "200": {
            "description": "Request captured. The status and body are the Source's configured response (200 by default)."
          }
        }
      },
      "get": {
        "operationId": "ingestSourceGet",
        "tags": ["Ingestion"],
        "summary": "Send a request to a source",
        "description": "Captures a GET request, like any other method: opening this URL in a browser or fetching it is saved as a request. To read a Source, use `GET /{id}` or `GET /{id}/requests` instead.",
        "security": [],
        "responses": {
          "200": {
            "description": "Request captured. The status and body are the Source's configured response (200 by default)."
          }
        }
      },
      "head": {
        "operationId": "ingestSourceHead",
        "tags": ["Ingestion"],
        "summary": "Send a request to a source",
        "description": "Captures a HEAD request, like any other method.",
        "security": [],
        "responses": {
          "200": {
            "description": "Request captured. The status is the Source's configured response (200 by default)."
          }
        }
      },
      "put": {
        "operationId": "ingestSourcePut",
        "tags": ["Ingestion"],
        "summary": "Send a request to a source",
        "description": "Captures the request. Response is the Source's configured response (200 by default).",
        "security": [],
        "requestBody": {
          "required": false,
          "description": "Any payload. The body is captured verbatim, whatever its content type.",
          "content": {
            "application/json": {
              "schema": {
                "example": {
                  "hello": "world"
                }
              }
            },
            "*/*": {}
          }
        },
        "responses": {
          "200": {
            "description": "Request captured."
          }
        }
      },
      "patch": {
        "operationId": "ingestSourcePatch",
        "tags": ["Ingestion"],
        "summary": "Send a request to a source",
        "description": "Captures the request. Response is the Source's configured response (200 by default).",
        "security": [],
        "requestBody": {
          "required": false,
          "description": "Any payload. The body is captured verbatim, whatever its content type.",
          "content": {
            "application/json": {
              "schema": {
                "example": {
                  "hello": "world"
                }
              }
            },
            "*/*": {}
          }
        },
        "responses": {
          "200": {
            "description": "Request captured."
          }
        }
      },
      "delete": {
        "operationId": "ingestSourceDelete",
        "tags": ["Ingestion"],
        "summary": "Send a request to a source",
        "description": "Captures the request. Response is the Source's configured response (200 by default).",
        "security": [],
        "responses": {
          "200": {
            "description": "Request captured."
          }
        }
      }
    },
    "/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The Source id without its `src_` prefix (the value embedded in the `inspect_url` returned by createSource)."
        }
      ],
      "get": {
        "operationId": "inspectSource",
        "tags": ["Inspection"],
        "summary": "Inspect a source",
        "description": "A Source. With `?format=json` (or `Accept: application/json`), the Source and its 10 newest requests as JSON, with `requests_url` to wait for the next one. Anything else gets the Inspect page. Never captured as a request.",
        "security": [],
        "responses": {
          "200": {
            "description": "The Inspect page (for a browser navigation) or a JSON preview of the Source and its recent captured requests (for `Accept: application/json`).",
            "content": {
              "text/html": {},
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourcePreview"
                }
              }
            }
          },
          "404": {
            "description": "No Inspect Source with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls to this Source from you in the last minute: up to 60 a minute for each caller and Source. Retry the same URL after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not read the Source. Retry the same URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Too many reads right now. Retry the same URL after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The Source id, from its inspect URL.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "`json` for JSON whatever the Accept header.",
            "schema": {
              "type": "string",
              "enum": ["json"]
            }
          }
        ]
      },
      "post": {
        "operationId": "inspectSourceWrite",
        "tags": ["Inspection"],
        "summary": "Not for sending requests",
        "description": "Writes to the inspect URL are not captured. Send requests to the ingest URL instead (also PUT, PATCH and DELETE).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The Source id, from its inspect URL.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "405": {
            "description": "Use the ingest URL, given as `ingest_url`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/{id}/requests": {
      "get": {
        "operationId": "listSourceRequests",
        "tags": ["Inspection"],
        "summary": "Read and wait for requests",
        "description": "The requests not yet returned to the `next` cursor, oldest first, each once. Without `next`, from the start. When there are none and `timeout` is set, holds the call open up to `timeout` seconds and returns as soon as one arrives; on timeout, returns `WaitTimeout` with a `resume` URL. Requests can become readable a moment after they arrive and not always in order; a request up to 15 seconds late is still returned. Always `Cache-Control: no-store`.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The Source id, from its inspect URL.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "next",
            "in": "query",
            "required": false,
            "description": "The cursor from a previous response's `next_url` or `resume`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "required": false,
            "description": "Seconds to wait for a request, 0 to 90. Omitted or 0: read once and return.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 90,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Most requests per response.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Requests, or a timeout reply.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/RequestsPage"
                    },
                    {
                      "$ref": "#/components/schemas/WaitTimeout"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An invalid `timeout`, `limit` or `next`. `read_url` starts again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No Inspect Source with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls to this Source from you in the last minute: up to 60 a minute for each caller and Source. Retry the same URL after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not check the Source. Retry the same URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Too many reads right now. Retry the same URL after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/{id}/requests/{request_id}/body": {
      "get": {
        "tags": ["Inspection"],
        "summary": "Download a request's body",
        "description": "The request's body exactly as it arrived, text or binary, as a file download (`application/octet-stream`). The request's `content-type` header says what it was. Each request's `body_url` points here.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The Source id, from its inspect URL.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "description": "The request's id (`req_…`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The body's exact bytes.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No request with this id on this Source.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls to this Source from you in the last minute: up to 60 a minute for each caller and Source. Retry the same URL after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not read the body. Retry the same URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Too many reads right now. Retry the same URL after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CreateSourceRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "maxLength": 64,
            "description": "Optional Source Type to pin (UPPER CASE, e.g. \"SHOPIFY\", \"STRIPE\"). Omitted → a generic WEBHOOK source. A value that isn't a supported Source Type (or is an auth-required type) returns 422.",
            "example": "SHOPIFY"
          },
          "name": {
            "type": "string",
            "maxLength": 155,
            "pattern": "^[A-z0-9-_]+$",
            "description": "Optional source name. Letters, digits, underscores and hyphens; anything else returns 422. This is the same rule the platform Source API applies, so a name accepted here is accepted there. Defaults to \"source\" and is auto-suffixed if a Source with that name already exists in the same guest account.",
            "example": "Example"
          }
        }
      },
      "Source": {
        "type": "object",
        "required": ["ingest_url", "inspect_url", "type", "name", "cli_key", "cmd_hint"],
        "properties": {
          "ingest_url": {
            "type": "string",
            "format": "uri",
            "description": "The Source's ingestion URL (inspect.hookdeck.com/e/<id>). Deliver requests of any configured verb here to have them captured.",
            "example": "https://inspect.hookdeck.com/e/d12t0ffw5id1cm"
          },
          "inspect_url": {
            "type": "string",
            "format": "uri",
            "description": "The Source's inspection URL — the bin (inspect.hookdeck.com/<id>). GET it to view captured requests (the Inspect page, or JSON with `Accept: application/json`). Matches the `Location` header.",
            "example": "https://inspect.hookdeck.com/d12t0ffw5id1cm"
          },
          "type": {
            "type": "string",
            "description": "The resolved Source Type.",
            "example": "SHOPIFY"
          },
          "name": {
            "type": "string",
            "example": "Example"
          },
          "cli_key": {
            "type": "string",
            "description": "Key that connects the Hookdeck CLI to this Source's guest account, so `hookdeck listen` can forward its requests.",
            "example": "123123123"
          },
          "cmd_hint": {
            "type": "string",
            "description": "A ready-to-run Hookdeck CLI command with the cli_key and the source name filled in. The name is shell-quoted, so the command can be run as given. `[port]` is the one placeholder to substitute — `hookdeck listen` takes a port or forwarding URL as its first argument.",
            "example": "npx hookdeck-cli listen [port] Source --path /webhooks --cli-key 123123123"
          },
          "requests_url": {
            "type": "string",
            "format": "uri",
            "description": "GET it to wait for the Source's next request: it waits up to 25 seconds and returns it as JSON.",
            "example": "https://inspect.hookdeck.com/d12t0ffw5id1cm/requests?timeout=25"
          },
          "note": {
            "type": "string",
            "description": "What to do next, in a sentence or two."
          }
        }
      },
      "SourcePreview": {
        "type": "object",
        "description": "A Source and its newest requests, with the calls to read and wait for more.",
        "required": [
          "id",
          "name",
          "type",
          "inspect_url",
          "ingest_url",
          "requests_url",
          "read_url",
          "requests"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The Source id.",
            "example": "src_d12t0ffw5id1cm"
          },
          "name": {
            "type": "string",
            "example": "Example"
          },
          "type": {
            "type": "string",
            "example": "SHOPIFY"
          },
          "inspect_url": {
            "type": "string",
            "format": "uri",
            "example": "https://inspect.hookdeck.com/d12t0ffw5id1cm"
          },
          "ingest_url": {
            "type": "string",
            "format": "uri",
            "description": "The Source's ingestion URL (inspect.hookdeck.com/e/<id>).",
            "example": "https://inspect.hookdeck.com/e/d12t0ffw5id1cm"
          },
          "note": {
            "type": "string",
            "description": "What has arrived and what to call next."
          },
          "requests_url": {
            "type": "string",
            "format": "uri",
            "description": "Waits for the next request after those shown."
          },
          "read_url": {
            "type": "string",
            "format": "uri",
            "description": "Reads every request from the start."
          },
          "requests": {
            "type": "array",
            "description": "The 10 newest requests, newest first (without `body_raw`).",
            "items": {
              "$ref": "#/components/schemas/CapturedRequest"
            }
          }
        }
      },
      "CapturedRequest": {
        "type": "object",
        "description": "A single captured request (an ingested HTTP request to the Source), from the Request plus its ShortEventData.",
        "properties": {
          "id": {
            "type": "string",
            "example": "req_9x2q1m0abc"
          },
          "verb": {
            "type": "string",
            "description": "Captured HTTP method (derived from the `x-hookdeck-original-method` header; defaults to POST).",
            "example": "POST"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the request was ingested.",
            "example": "2026-07-24T12:34:56Z"
          },
          "already_arrived": {
            "type": "boolean",
            "description": "True when the request came in before this call started. It's still a real request to this URL, and counts as the one you were waiting for. In `/{id}/requests` responses only.",
            "example": false
          },
          "path": {
            "type": "string",
            "example": "/webhooks"
          },
          "query": {
            "type": "string",
            "example": "foo=bar"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Captured request headers."
          },
          "body": {
            "description": "Captured request body, parsed when it is JSON (string or JSON).",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object"
              }
            ],
            "nullable": true
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the request passed the Source's verification."
          },
          "rejection_cause": {
            "type": "string",
            "nullable": true,
            "description": "Why the request was rejected, if it was (e.g. UNSUPPORTED_HTTP_METHOD). Null when accepted, including when the Source has no destination yet."
          },
          "method": {
            "type": "string",
            "description": "Captured HTTP method. Same as `verb`.",
            "example": "POST"
          },
          "body_raw": {
            "type": "string",
            "nullable": true,
            "description": "The body exactly as it arrived, for a text body up to 1 MB; null for a binary body, one too large to include (past 1 MB, or past 2 MB of bodies in one reply), or one that couldn't be read, which `body_url` downloads. In `/{id}/requests` responses only."
          },
          "body_base64": {
            "type": "string",
            "format": "byte",
            "description": "A binary body's exact bytes, base64-encoded, while it's small (up to 64 KB, within 2 MB of bodies for one reply). Otherwise download it from `body_url`. In `/{id}/requests` responses only."
          },
          "body_read_failed": {
            "type": "boolean",
            "description": "Present and true when the body exactly as it arrived couldn't be read just now: `body_raw` is null, and `body` is included only if it fits. Download it from `body_url`. In `/{id}/requests` responses only."
          },
          "body_url": {
            "type": "string",
            "format": "uri",
            "description": "Downloads the body exactly as it arrived, text or binary."
          }
        }
      },
      "UnprocessableEntityError": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "UNPROCESSABLE_ENTITY"
          },
          "status": {
            "type": "integer",
            "example": 422
          },
          "data": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "One or more validation messages.",
            "example": ["type must be one of [WEBHOOK, SHOPIFY, STRIPE]"]
          }
        }
      },
      "RateLimitError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Too Many Requests"
          },
          "message": {
            "type": "string",
            "example": "Limited to 60 source creations per minute."
          },
          "retry_after": {
            "type": "integer",
            "description": "Seconds to wait before retrying.",
            "example": 30
          }
        }
      },
      "RequestsPage": {
        "type": "object",
        "description": "Requests not yet returned to this cursor, oldest first.",
        "required": ["requests", "more", "next_url"],
        "properties": {
          "requests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CapturedRequest"
            }
          },
          "more": {
            "type": "boolean",
            "description": "More requests are ready now: call `next_url` straight away."
          },
          "note": {
            "type": "string"
          },
          "next_url": {
            "type": "string",
            "format": "uri",
            "description": "Continues after these requests, waiting up to 25 seconds."
          }
        }
      },
      "WaitTimeout": {
        "type": "object",
        "description": "No new request arrived within the timeout. This is normal: call `resume` to keep waiting.",
        "required": ["timed_out", "requests", "resume", "read"],
        "properties": {
          "timed_out": {
            "type": "boolean",
            "enum": [true]
          },
          "requests": {
            "type": "array",
            "maxItems": 0,
            "items": {}
          },
          "note": {
            "type": "string"
          },
          "resume": {
            "type": "string",
            "format": "uri",
            "description": "Waits again from the same point. Changes on every call, so it is never served from a cache."
          },
          "read": {
            "type": "string",
            "format": "uri",
            "description": "The Source and its newest requests as JSON."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error", "note"],
        "properties": {
          "error": {
            "type": "string",
            "example": "not_found"
          },
          "note": {
            "type": "string",
            "description": "What went wrong and what to do."
          },
          "read_url": {
            "type": "string",
            "format": "uri"
          },
          "ingest_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "CreateSourceError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong."
          }
        }
      }
    }
  }
}
