{
  "openapi": "3.1.1",
  "info": {
    "title": "EmailOnSteroids API",
    "version": "1.1.0",
    "summary": "Render an email across emulated inbox clients.",
    "description": "Render one email across emulated inbox clients and read back a screenshot\nper client.\n\n**Rendering is asynchronous.** `POST /tests` returns immediately with\n`status: \"queued\"` and one preview per (client profile × colour mode) — up\nto 32. Poll `GET /tests/{id}` (or subscribe to `GET /tests/{id}/events`)\nuntil rendering is terminal. Entitled QA analyzers run independently; wait until\n`analysisCounts.queued` and `analysisCounts.processing` are both zero before\ntreating all analysis evidence as final.\n\n**Every screenshot is emulated.** Previews are produced by headless\nChromium with a per-client transformation profile — not by the real client\non a real device. Each signed preview carries a `fidelity` block saying how\ndefensible that particular emulation is (`class`), what the profile claims\nto represent, what it does not model (`limitations`), and whether anyone\nhas compared it to the real client (`evidenceStatus`, `validatedAt`).\n\n**Every response uses the same envelope** — `success`, `data`, `error`,\n`meta` — on success and on failure alike. Branch on `error.code`, never on\nthe HTTP status alone. Every response also carries an `X-Request-Id`\nheader, echoed as `meta.requestId`; it is the only thing to quote in a\nsupport request.\n\n**Agents should prefer the MCP server at `/mcp`**, which exposes the same\nservice layer as five tools over Streamable HTTP and authenticates with the\nsame API keys.",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://app.emailonsteroids.io/v1",
      "description": "This deployment."
    }
  ],
  "tags": [
    {
      "name": "Tests",
      "description": "Submitting email HTML and watching it render."
    },
    {
      "name": "Previews",
      "description": "Reading individual screenshots."
    },
    {
      "name": "Share links",
      "description": "Expiring public pages for human reviewers."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/tests": {
      "post": {
        "operationId": "createTest",
        "tags": [
          "Tests"
        ],
        "summary": "Submit an email for rendering",
        "description": "Queues one render per (client profile × colour mode) and returns the\ntest immediately, before any of them has run.\n\nOmit `profiles` to render the whole matrix the plan allows — that is\nalmost always what you want. Pass it only to re-test specific clients.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTestRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Queued. `status` is `queued` and `previews` holds one row per render, each with its own status.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data",
                    "error",
                    "meta"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Test"
                    },
                    "error": {
                      "type": "null"
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "requestId"
                      ],
                      "properties": {
                        "requestId": {
                          "type": "string",
                          "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                          "examples": [
                            "req_9f3c1a7b2e04"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not validate. `error.details` names each offending field.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listTests",
        "tags": [
          "Tests"
        ],
        "summary": "List this account's tests",
        "description": "Newest first, cursor-paginated. `previews` is omitted here — read a single test to get the full matrix.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of tests. `meta.nextCursor` is null on the last page.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data",
                    "error",
                    "meta"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Test"
                      }
                    },
                    "error": {
                      "type": "null"
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "requestId",
                        "nextCursor"
                      ],
                      "properties": {
                        "requestId": {
                          "type": "string",
                          "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                          "examples": [
                            "req_9f3c1a7b2e04"
                          ]
                        },
                        "nextCursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque cursor for the next page, or null on the last page. Pass it back as `?cursor=`. Do not decode it: the sort key it encodes is an implementation detail and will change."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not validate. `error.details` names each offending field.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tests/{id}": {
      "get": {
        "operationId": "getTest",
        "tags": [
          "Tests"
        ],
        "summary": "Read one test and every client's status",
        "description": "The endpoint to poll while a run is in flight — about every two\nseconds. A whole matrix normally completes in 20–60 seconds.\n\n`counts` is the per-status tally, so progress can be reported without\nreducing the previews array on every tick.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The test, with its previews.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data",
                    "error",
                    "meta"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Test"
                    },
                    "error": {
                      "type": "null"
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "requestId"
                      ],
                      "properties": {
                        "requestId": {
                          "type": "string",
                          "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                          "examples": [
                            "req_9f3c1a7b2e04"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource for this account. A test belonging to another account reports as 404, never 403 — an authenticated caller must not be able to probe which ids exist.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tests/{id}/rerun": {
      "post": {
        "operationId": "rerunTest",
        "tags": [
          "Tests"
        ],
        "summary": "Render the same campaign again",
        "description": "Creates a **new** test from the stored HTML and returns it — the\noriginal run is left untouched, so a link someone was already given\nkeeps showing what they were told to look at.\n\nDeliberately not idempotent: two calls are two runs, which is what the\nverb means here. It costs the same rate-limit budget as submitting a\nnew email, because it queues the same fan-out.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TestId"
          }
        ],
        "responses": {
          "201": {
            "description": "The new test, queued. Note the id differs from the one in the path.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data",
                    "error",
                    "meta"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Test"
                    },
                    "error": {
                      "type": "null"
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "requestId"
                      ],
                      "properties": {
                        "requestId": {
                          "type": "string",
                          "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                          "examples": [
                            "req_9f3c1a7b2e04"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource for this account. A test belonging to another account reports as 404, never 403 — an authenticated caller must not be able to probe which ids exist.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tests/{id}/events": {
      "get": {
        "operationId": "streamTestEvents",
        "tags": [
          "Tests"
        ],
        "summary": "Subscribe to a test's progress (SSE)",
        "description": "A Server-Sent Events stream, for when polling is the wrong shape.\nAuthorisation happens before the stream opens, so a 404 arrives as a\n404 rather than as an event on a 200.\n\nEvent types, in the order they occur:\n\n- `snapshot` — `{ test, previews }`, sent once on connect.\n- `preview.updated` — `{ testId, preview }`, one per client that\n  changed status.\n- `analysis.updated` — `{ testId, analysis, counts }`, when one QA\n  analyzer changes status or evidence.\n- `test.updated` — `{ testId, status, counts }`, when the roll-up\n  moves.\n- `done` — `{ testId, status }`. The stream closes after it.\n- `timeout` — `{ testId }` after ~290s. Not an error: reconnect and a\n  fresh `snapshot` arrives.\n\nA comment heartbeat every 25s keeps intermediaries from closing an\nidle connection; `EventSource` ignores it.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The event stream.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE frames: `event: <type>` followed by a JSON `data:` line."
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource for this account. A test belonging to another account reports as 404, never 403 — an authenticated caller must not be able to probe which ids exist.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tests/{id}/previews/{client}": {
      "get": {
        "operationId": "getPreview",
        "tags": [
          "Previews"
        ],
        "summary": "Read one client's screenshot",
        "description": "Returns a **signed, short-lived URL** rather than image bytes, so the\nsame response works for a browser, a script and an agent.\n\n`url` is null while the client is still rendering and when it failed —\ncheck `status` and `error` before treating a null URL as a bug.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TestId"
          },
          {
            "$ref": "#/components/parameters/ClientKey"
          },
          {
            "$ref": "#/components/parameters/PreviewMode"
          }
        ],
        "responses": {
          "200": {
            "description": "The preview and its signed URLs.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data",
                    "error",
                    "meta"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/SignedPreview"
                    },
                    "error": {
                      "type": "null"
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "requestId"
                      ],
                      "properties": {
                        "requestId": {
                          "type": "string",
                          "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                          "examples": [
                            "req_9f3c1a7b2e04"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such test for this account, or this client was never rendered in this mode — a light-only client has no dark preview.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not validate. `error.details` names each offending field.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tests/{id}/share": {
      "post": {
        "operationId": "createShareLink",
        "tags": [
          "Share links"
        ],
        "summary": "Mint a self-destructing public link",
        "description": "Creates a public page showing every preview for the test. **The slug\nis the credential** — anyone holding the URL can see the previews\nuntil it expires, so share it deliberately.\n\nCreate the link once the test is `done`. One created earlier still\nworks, but whoever opens it may find an unfinished grid. Each call\nmints a new link; it does not return an existing one.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TestId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateShareLinkRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new link.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data",
                    "error",
                    "meta"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/ShareLink"
                    },
                    "error": {
                      "type": "null"
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "requestId"
                      ],
                      "properties": {
                        "requestId": {
                          "type": "string",
                          "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                          "examples": [
                            "req_9f3c1a7b2e04"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, an unknown key, or a revoked key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource for this account. A test belonging to another account reports as 404, never 403 — an authenticated caller must not be able to probe which ids exist.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not validate. `error.details` names each offending field.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              },
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer",
                  "examples": [
                    42
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something failed on our side. The message never quotes your HTML; quote `meta.requestId` when reporting it.",
            "headers": {
              "X-Request-Id": {
                "description": "The request identifier, also present as `meta.requestId`.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "req_9f3c1a7b2e04"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "eos_live_…",
        "description": "An API key, sent as `Authorization: Bearer eos_live_…`.\n\nCreate one in the dashboard under API keys. The key is shown once and\nstored only as a hash, so it cannot be recovered — rotate rather than\nrecover. Every failure mode (no key, unknown key, revoked key) answers with\nthe same 401, deliberately: distinguishing them would hand out a\nkey-existence oracle."
      }
    },
    "parameters": {
      "TestId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The test id returned when it was created.",
        "schema": {
          "type": "string",
          "format": "uuid",
          "examples": [
            "6f1d2c4a-9b3e-4f27-8f0a-1c2b3d4e5f60"
          ]
        }
      },
      "ClientKey": {
        "name": "client",
        "in": "path",
        "required": true,
        "description": "Client profile key. A key outside the account's plan is refused, not silently skipped.",
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
          "examples": [
            "outlook-365-win"
          ]
        }
      },
      "PreviewMode": {
        "name": "mode",
        "in": "query",
        "required": false,
        "description": "Colour scheme. Not every client renders dark; asking for a mode a client does not support is a 404.",
        "schema": {
          "type": "string",
          "enum": [
            "light",
            "dark"
          ],
          "default": "light"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size. Out-of-range values are clamped to 100 rather than rejected, so asking for 1000 returns a full page and a cursor.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The `meta.nextCursor` from the previous page.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The failure envelope. `data` is always null and `error.code` is the machine-readable reason — branch on it, never on the message.",
        "required": [
          "success",
          "data",
          "error",
          "meta"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "data": {
            "type": "null"
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "validation_failed",
                  "payload_too_large",
                  "payment_required",
                  "rate_limited",
                  "internal_error"
                ],
                "description": "`unauthorized` → 401 · `forbidden` → 403 · `not_found` → 404 · `conflict` → 409 · `validation_failed` → 422 · `payload_too_large` → 413 · `payment_required` → 402 · `rate_limited` → 429 · `internal_error` → 500"
              },
              "message": {
                "type": "string",
                "description": "Human-readable and safe to display. It never quotes the HTML you submitted.",
                "examples": [
                  "Request body is invalid."
                ]
              },
              "details": {
                "type": "array",
                "description": "Field-level problems, present on `validation_failed`.",
                "items": {
                  "type": "object",
                  "required": [
                    "path",
                    "message"
                  ],
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Dotted path to the offending field, or `(body)`.",
                      "examples": [
                        "profiles.0"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "meta": {
            "type": "object",
            "required": [
              "requestId"
            ],
            "properties": {
              "requestId": {
                "type": "string",
                "description": "Echoed in the X-Request-Id header. Quote it in a support request — it identifies the request without identifying its contents.",
                "examples": [
                  "req_9f3c1a7b2e04"
                ]
              }
            }
          }
        }
      },
      "PreviewCounts": {
        "type": "object",
        "description": "Per-status tally of this test's previews, so progress can be reported without walking the previews array.",
        "required": [
          "queued",
          "processing",
          "done",
          "failed"
        ],
        "properties": {
          "queued": {
            "type": "integer",
            "minimum": 0
          },
          "processing": {
            "type": "integer",
            "minimum": 0
          },
          "done": {
            "type": "integer",
            "minimum": 0
          },
          "failed": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "SourceLocation": {
        "type": "object",
        "required": [
          "line",
          "column",
          "excerpt"
        ],
        "properties": {
          "line": {
            "type": "integer",
            "minimum": 1,
            "description": "1-based source line."
          },
          "column": {
            "type": "integer",
            "minimum": 1,
            "description": "1-based column within the line."
          },
          "excerpt": {
            "type": "string",
            "description": "The trimmed source line, capped in length."
          }
        }
      },
      "ClientImpact": {
        "type": "object",
        "required": [
          "profileKey",
          "support",
          "note"
        ],
        "properties": {
          "profileKey": {
            "type": "string",
            "description": "Matches a key in the emulated client matrix."
          },
          "support": {
            "type": "string",
            "enum": [
              "supported",
              "partial",
              "unsupported",
              "unknown"
            ],
            "description": "`unknown` means the evidence records no result for this client. It is never a substitute for `supported`."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "The upstream caveat for this client, when one exists."
          }
        }
      },
      "CompatibilityDetail": {
        "type": "object",
        "description": "Present only on findings from the compatibility analyzer.",
        "required": [
          "featureId",
          "featureTitle",
          "feature",
          "locations",
          "occurrences",
          "clients",
          "remediation",
          "lastTested",
          "referenceUrl"
        ],
        "properties": {
          "featureId": {
            "type": "string",
            "description": "Upstream feature identifier, e.g. `css-display-flex`."
          },
          "featureTitle": {
            "type": "string"
          },
          "feature": {
            "type": "string",
            "description": "The property, element or function detected."
          },
          "locations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SourceLocation"
            },
            "description": "Where the feature appears. Capped; `occurrences` stays exact."
          },
          "occurrences": {
            "type": "integer",
            "minimum": 1
          },
          "clients": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ClientImpact"
            },
            "description": "One entry per profile in the emulated client matrix."
          },
          "remediation": {
            "type": "string",
            "maxLength": 240
          },
          "lastTested": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The date the evidence source last tested this feature. Often years old, and exposed rather than hidden for that reason."
          },
          "referenceUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "AnalysisProvenance": {
        "type": "object",
        "required": [
          "source",
          "evidenceVersion",
          "retrievedAt",
          "stale"
        ],
        "properties": {
          "source": {
            "type": "string",
            "example": "caniemail.com"
          },
          "evidenceVersion": {
            "type": "string",
            "example": "caniemail@1.0.4+2026-08-10",
            "description": "Pins the dataset a result was computed against. Identical input, analyzer version and evidence version reproduce identical findings."
          },
          "retrievedAt": {
            "type": "string",
            "format": "date-time"
          },
          "stale": {
            "type": "boolean",
            "description": "True when the evidence could not be refreshed and a bundled copy was used instead."
          }
        }
      },
      "AnalysisFinding": {
        "type": "object",
        "required": [
          "code",
          "severity",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine-readable finding code."
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "warning",
              "error"
            ]
          },
          "message": {
            "type": "string",
            "description": "Safe customer-facing explanation."
          },
          "target": {
            "type": "string",
            "description": "Affected URL or element when applicable."
          },
          "evidence": {
            "type": "string",
            "description": "Deterministic rule evidence when applicable."
          },
          "compatibility": {
            "$ref": "#/components/schemas/CompatibilityDetail"
          }
        }
      },
      "AnalysisEvidence": {
        "type": "object",
        "required": [
          "summary",
          "score",
          "checkedCount",
          "findings"
        ],
        "properties": {
          "summary": {
            "type": "string"
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 100
          },
          "checkedCount": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalysisFinding"
            }
          },
          "provenance": {
            "$ref": "#/components/schemas/AnalysisProvenance"
          }
        }
      },
      "AnalysisJob": {
        "type": "object",
        "description": "One independently retried, versioned email QA analyzer.",
        "required": [
          "id",
          "type",
          "version",
          "catalogVersion",
          "status",
          "attempts",
          "evidence",
          "error",
          "startedAt",
          "completedAt",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "examples": [
              "6f1d2c4a-9b3e-4f27-8f0a-1c2b3d4e5f60"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "spam",
              "links",
              "images",
              "compatibility"
            ]
          },
          "version": {
            "type": "string",
            "description": "Immutable analyzer implementation version."
          },
          "catalogVersion": {
            "type": "string",
            "description": "Entitlement catalog that queued this job."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "done",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer",
            "minimum": 0
          },
          "evidence": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AnalysisEvidence"
              },
              {
                "type": "null"
              }
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          }
        }
      },
      "Preview": {
        "type": "object",
        "description": "One client profile rendered in one colour mode.",
        "required": [
          "id",
          "profileKey",
          "mode",
          "status",
          "screenshotPath",
          "thumbnailPath",
          "width",
          "height",
          "renderMs",
          "error"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "examples": [
              "6f1d2c4a-9b3e-4f27-8f0a-1c2b3d4e5f60"
            ]
          },
          "profileKey": {
            "type": "string",
            "description": "Client profile key — a lowercase slug.",
            "examples": [
              "outlook-365-win"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "light",
              "dark"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "done",
              "failed"
            ],
            "description": "This client's own status. One client failing does not fail the run."
          },
          "screenshotPath": {
            "type": [
              "string",
              "null"
            ],
            "description": "Storage path, not a URL. Read the image through the preview endpoint, which returns a signed URL."
          },
          "thumbnailPath": {
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pixel width of the full-height screenshot."
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "renderMs": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How long this client took to render, in milliseconds."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why this client failed. Null unless `status` is failed."
          }
        }
      },
      "SignedPreview": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Preview"
          },
          {
            "type": "object",
            "required": [
              "url",
              "thumbnailUrl",
              "urlExpiresIn"
            ],
            "properties": {
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "Signed URL for the full screenshot, valid for 600 seconds. Null while the client is still rendering and when it failed. Do not store it — request a fresh one instead."
              },
              "thumbnailUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri"
              },
              "urlExpiresIn": {
                "type": "integer",
                "description": "Seconds the URLs above remain valid.",
                "examples": [
                  600
                ]
              },
              "fidelity": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Fidelity"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "What this screenshot actually is. Null only when the client profile row is missing — never as a claim that the capture was real."
              }
            }
          }
        ]
      },
      "Fidelity": {
        "type": "object",
        "description": "How defensible one emulated client profile's claim is. Every preview this API returns is produced by headless Chromium with a per-client transformation profile; nothing here is captured from a real device.",
        "required": [
          "emulated",
          "class",
          "represents",
          "limitations",
          "evidenceStatus",
          "validatedAt",
          "transformationVersion"
        ],
        "properties": {
          "emulated": {
            "const": true,
            "description": "Always true. Reserved for a future real-capture tier."
          },
          "class": {
            "type": "string",
            "enum": [
              "engine_faithful",
              "approximated",
              "variant",
              "experimental"
            ],
            "description": "engine_faithful — our renderer is the client's own engine family, so support differences are real rather than modelled. approximated — the engine is modelled by a transformation table; layout is close, not identical (every Word-engine Outlook). variant — a viewport variant sharing another profile's rules. experimental — added but not yet measured or calibrated.",
            "examples": [
              "approximated"
            ]
          },
          "represents": {
            "type": "object",
            "description": "The app, OS and device this profile claims to stand for.",
            "required": [
              "app",
              "appVersion",
              "os",
              "browser",
              "device"
            ],
            "properties": {
              "app": {
                "type": "string",
                "examples": [
                  "Outlook"
                ]
              },
              "appVersion": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "2019 (16.x)"
                ]
              },
              "os": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "Windows 10"
                ]
              },
              "browser": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Null for native apps — a mail app has no browser."
              },
              "device": {
                "type": "string",
                "examples": [
                  "Desktop"
                ]
              }
            }
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What this profile does not model. May be empty.",
            "examples": [
              [
                "The Word engine is approximated by Chromium"
              ]
            ]
          },
          "evidenceStatus": {
            "type": "string",
            "enum": [
              "uncalibrated",
              "spot_checked",
              "calibrated"
            ],
            "description": "Whether a human has compared this profile to the real client.",
            "examples": [
              "uncalibrated"
            ]
          },
          "validatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When that comparison last happened. Null means never."
          },
          "transformationVersion": {
            "type": "string",
            "description": "The render rule set this profile was exercised under. Two runs are only comparable when this matches.",
            "examples": [
              "2026.08-1"
            ]
          }
        }
      },
      "Test": {
        "type": "object",
        "description": "A submitted email and the fan-out of client renders it queued.",
        "required": [
          "id",
          "campaignId",
          "subject",
          "origin",
          "status",
          "createdAt",
          "completedAt",
          "counts",
          "analysisCounts"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "examples": [
              "6f1d2c4a-9b3e-4f27-8f0a-1c2b3d4e5f60"
            ]
          },
          "campaignId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Groups repeated runs of one campaign."
          },
          "subject": {
            "type": "string",
            "maxLength": 500
          },
          "origin": {
            "type": "string",
            "enum": [
              "paste",
              "inbox",
              "api"
            ],
            "description": "How the HTML arrived: pasted in the app, forwarded to an ingest address, or submitted through this API (`api`, which includes MCP)."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "done",
              "failed"
            ],
            "description": "Roll-up over the previews: `queued` (nothing started), `processing` (some still rendering), `done` (all finished), `failed` (all failed)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          },
          "counts": {
            "$ref": "#/components/schemas/PreviewCounts"
          },
          "analysisCounts": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PreviewCounts"
              }
            ],
            "description": "Per-status analyzer tally, independent from preview progress."
          },
          "previews": {
            "type": "array",
            "description": "Every (profile × mode) row. Returned when reading a single test; omitted from the collection, where 32 rows per card is not a list.",
            "items": {
              "$ref": "#/components/schemas/Preview"
            }
          },
          "analysis": {
            "type": "array",
            "description": "Entitled analyzer jobs with independent status, version, and evidence. Returned for one test; omitted from the collection.",
            "items": {
              "$ref": "#/components/schemas/AnalysisJob"
            }
          }
        }
      },
      "CreateTestRequest": {
        "type": "object",
        "required": [
          "html"
        ],
        "additionalProperties": false,
        "properties": {
          "html": {
            "type": "string",
            "minLength": 1,
            "description": "The complete email HTML, exactly as it would be sent — a full document with its head and inline styles. At most 2097152 bytes (measured in bytes, not characters).",
            "examples": [
              "<!doctype html><html><body><h1>Hello</h1></body></html>"
            ]
          },
          "subject": {
            "type": "string",
            "maxLength": 500,
            "default": "",
            "description": "Shown in the preview chrome."
          },
          "campaignId": {
            "type": "string",
            "format": "uuid",
            "description": "Groups repeated runs of one campaign for comparison."
          },
          "profiles": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1
            },
            "description": "Client profile keys to render. Omit for the whole matrix the plan allows — an empty array is rejected rather than rendering nothing, because it almost always means a list was built dynamically and came back empty.",
            "examples": [
              [
                "gmail-web",
                "outlook-365-win"
              ]
            ]
          }
        }
      },
      "CreateShareLinkRequest": {
        "type": "object",
        "additionalProperties": false,
        "description": "The body is optional; sending none means the default TTL.",
        "properties": {
          "ttl": {
            "type": "string",
            "enum": [
              "24h",
              "7d",
              "30d"
            ],
            "default": "7d",
            "description": "How long the link lives. There is no permanent option — pick the shortest window that outlives the review."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-09-01T23:59:59.000Z"
            ],
            "description": "An exact instant to expire at, overriding ttl. At most 90 days out — a longer window is rejected, because 'self-destructing' has to stay an honest description."
          },
          "password": {
            "type": "string",
            "minLength": 4,
            "maxLength": 200,
            "description": "An optional second factor on top of the unguessable URL. Stored only as a hash, so it cannot be read back — pass it to the recipient by some route other than the link itself."
          }
        }
      },
      "ShareLink": {
        "type": "object",
        "description": "A public page showing every preview for a test. The slug IS the credential: anyone holding the URL can see the previews until it expires.",
        "required": [
          "id",
          "testId",
          "slug",
          "url",
          "expiresAt",
          "revokedAt",
          "viewCount",
          "createdAt",
          "requiresPassword"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "examples": [
              "6f1d2c4a-9b3e-4f27-8f0a-1c2b3d4e5f60"
            ]
          },
          "testId": {
            "type": "string",
            "format": "uuid",
            "examples": [
              "6f1d2c4a-9b3e-4f27-8f0a-1c2b3d4e5f60"
            ]
          },
          "slug": {
            "type": "string",
            "description": "The secret path segment. Treat it as a credential."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The full public URL. Needs no login."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ],
            "description": "When the link stops resolving. After this the page answers 410 and its public access is deleted — the run itself is untouched and stays in your account."
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          },
          "viewCount": {
            "type": "integer",
            "minimum": 0
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-08-08T09:31:04.512Z"
            ]
          },
          "requiresPassword": {
            "type": "boolean",
            "description": "Whether a visitor is asked for a password. The password itself is never returned by any endpoint."
          }
        }
      }
    }
  },
  "x-rate-limits": {
    "description": "Limits are per API key and per surface:\n\n- `POST /tests` — 20 requests per 60s. It is the expensive verb: one call fans out to up to 32 renders.\n- every other endpoint — 240 requests per 60s, generous enough that polling every two seconds is never throttled.\n\nExceeding a limit returns `429` with `error.code: \"rate_limited\"` and a\n`Retry-After` header in seconds. Honour it rather than inventing a backoff.",
    "create": {
      "limit": 20,
      "windowMs": 60000
    },
    "read": {
      "limit": 240,
      "windowMs": 60000
    },
    "mcp": {
      "limit": 240,
      "windowMs": 60000
    }
  },
  "x-mcp": {
    "description": "The same service layer as five MCP tools over Streamable HTTP, authenticated with the same API keys.",
    "endpoint": "https://app.emailonsteroids.io/mcp",
    "transport": "streamable-http"
  }
}
