{
  "openapi": "3.0.3",
  "info": {
    "title": "Peregrine API",
    "description": "Edge delivery and image optimisation for WordPress.",
    "version": "1.1.0"
  },
  "servers": [
    {
      "url": "https://api.peregrinecdn.com"
    }
  ],
  "tags": [
    {
      "name": "Discovery",
      "description": ""
    },
    {
      "name": "Sites",
      "description": ""
    },
    {
      "name": "Usage",
      "description": ""
    },
    {
      "name": "Edge",
      "description": ""
    },
    {
      "name": "Provisioning",
      "description": ""
    },
    {
      "name": "Analysis",
      "description": ""
    },
    {
      "name": "Uptime",
      "description": ""
    },
    {
      "name": "Governance",
      "description": ""
    },
    {
      "name": "Plugin (site key)",
      "description": ""
    }
  ],
  "components": {
    "securitySchemes": {
      "default": {
        "type": "http",
        "scheme": "bearer",
        "description": "Create one in the console at <a href=\"https://app.peregrinecdn.com/tokens\">app.peregrinecdn.com/tokens</a>, then send it as <code>Authorization: Bearer &lt;token&gt;</code>.<br><br>Tokens carry <b>abilities</b>, and the split is not read versus write - it is whether the call can change what visitors are served or what the account is billed. <code>read</code>, <code>purge</code> and <code>analyse</code> are the default set; <code>sites</code>, <code>warm</code> and <code>provision</code> have to be granted deliberately. A refusal is a <code>403</code> naming the ability that was missing, so a client can correct itself rather than stopping to ask somebody to read this page."
      }
    }
  },
  "security": [
    {
      "default": []
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "summary": "Liveness.",
        "operationId": "liveness",
        "description": "Answers without a credential, and says nothing a stranger should not see.\nThis is the only route the documentation generator is allowed to call\nfor real; every other example is written by hand, so that generating\ndocs can never reach a customer's data.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "ok": true,
                    "service": "peregrine-control-plane",
                    "version": "1.1.0",
                    "db": "peregrine"
                  },
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "service": {
                      "type": "string",
                      "example": "peregrine-control-plane"
                    },
                    "version": {
                      "type": "string",
                      "example": "1.1.0"
                    },
                    "db": {
                      "type": "string",
                      "example": "peregrine"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Discovery"
        ],
        "security": []
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "summary": "The OpenAPI document.",
        "operationId": "theOpenAPIDocument",
        "description": "Public, because a client needs to read it before it has a credential, and\nit describes shapes rather than data.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "openapi": "3.0.3",
                    "info": {
                      "title": "Peregrine API",
                      "version": "1.1.0"
                    },
                    "paths": []
                  },
                  "properties": {
                    "openapi": {
                      "type": "string",
                      "example": "3.0.3"
                    },
                    "info": {
                      "type": "object",
                      "properties": {
                        "title": {
                          "type": "string",
                          "example": "Peregrine API"
                        },
                        "version": {
                          "type": "string",
                          "example": "1.1.0"
                        }
                      }
                    },
                    "paths": {
                      "type": "object",
                      "properties": []
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Discovery"
        ],
        "security": []
      }
    },
    "/api/v1/meta": {
      "get": {
        "summary": "What this token can do.",
        "operationId": "whatThisTokenCanDo",
        "description": "The first call any client should make. Returns the account, the abilities\nthis token actually holds, the sites it can address, the warm-region\ncatalogue, and the conventions of the API - so a program can work without\nbeing told about it in advance.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "version": "1.1.0",
                    "account": {
                      "id": 42,
                      "name": "Acme Digital",
                      "kind": "agency",
                      "plan": "dive"
                    },
                    "token": {
                      "name": "Acme automation",
                      "authenticated_as": "api token",
                      "abilities": [
                        "read",
                        "purge",
                        "analyse"
                      ],
                      "last_used_at": "2026-09-15T08:14:02.000000Z",
                      "expires_at": null
                    },
                    "sites": [
                      {
                        "id": 7,
                        "label": "example-co-uk",
                        "domain": "example.co.uk",
                        "status": "active",
                        "cdn_base": "https://images.example.co.uk"
                      }
                    ],
                    "warm_regions": [
                      "UK",
                      "DE",
                      "US-East",
                      "Singapore"
                    ],
                    "conventions": {
                      "site_reference": "Pass the numeric id, the label, or the domain.",
                      "bytes": "Totals are reconciled server-side. Use them as given.",
                      "benchmark": "benchmark_modelled is our measured head-to-head, not a live competitor result.",
                      "delivery": "Markup proves nothing. POST /v1/sites/{site}/verify is the evidence.",
                      "ttfb": "Quote the measured improvement between two points, never an absolute figure."
                    }
                  },
                  "properties": {
                    "version": {
                      "type": "string",
                      "example": "1.1.0"
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "example": 42
                        },
                        "name": {
                          "type": "string",
                          "example": "Acme Digital"
                        },
                        "kind": {
                          "type": "string",
                          "example": "agency"
                        },
                        "plan": {
                          "type": "string",
                          "example": "dive"
                        }
                      }
                    },
                    "token": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "example": "Acme automation"
                        },
                        "authenticated_as": {
                          "type": "string",
                          "example": "api token"
                        },
                        "abilities": {
                          "type": "array",
                          "example": [
                            "read",
                            "purge",
                            "analyse"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "last_used_at": {
                          "type": "string",
                          "example": "2026-09-15T08:14:02.000000Z"
                        },
                        "expires_at": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        }
                      }
                    },
                    "sites": {
                      "type": "array",
                      "example": [
                        {
                          "id": 7,
                          "label": "example-co-uk",
                          "domain": "example.co.uk",
                          "status": "active",
                          "cdn_base": "https://images.example.co.uk"
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer",
                            "example": 7
                          },
                          "label": {
                            "type": "string",
                            "example": "example-co-uk"
                          },
                          "domain": {
                            "type": "string",
                            "example": "example.co.uk"
                          },
                          "status": {
                            "type": "string",
                            "example": "active"
                          },
                          "cdn_base": {
                            "type": "string",
                            "example": "https://images.example.co.uk"
                          }
                        }
                      }
                    },
                    "warm_regions": {
                      "type": "array",
                      "example": [
                        "UK",
                        "DE",
                        "US-East",
                        "Singapore"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "conventions": {
                      "type": "object",
                      "properties": {
                        "site_reference": {
                          "type": "string",
                          "example": "Pass the numeric id, the label, or the domain."
                        },
                        "bytes": {
                          "type": "string",
                          "example": "Totals are reconciled server-side. Use them as given."
                        },
                        "benchmark": {
                          "type": "string",
                          "example": "benchmark_modelled is our measured head-to-head, not a live competitor result."
                        },
                        "delivery": {
                          "type": "string",
                          "example": "Markup proves nothing. POST /v1/sites/{site}/verify is the evidence."
                        },
                        "ttfb": {
                          "type": "string",
                          "example": "Quote the measured improvement between two points, never an absolute figure."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "no token, or a revoked one",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "message": "Unauthenticated."
                  },
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Unauthenticated."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Discovery"
        ]
      }
    },
    "/api/v1/account": {
      "get": {
        "summary": "Account, plan and entitlement.",
        "operationId": "accountPlanAndEntitlement",
        "description": "Includes what has been paid for. Where no order exists there is no\nceiling, deliberately: blocking someone who has paid costs them what they\nbought today, while letting one extra through costs a little unbilled\nusage.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 42,
                    "name": "Acme Digital",
                    "email": "ops@acme.example",
                    "kind": "agency",
                    "plan": "dive",
                    "status": "active",
                    "entitlement": {
                      "sites_bought": 25,
                      "sites_in_use": 11,
                      "warm_locations": 3,
                      "shield": true
                    },
                    "usage": {
                      "images": 54874,
                      "bytes": {
                        "original": 12020896087,
                        "optimised": 1169791846,
                        "saved": 10851104241,
                        "benchmark_modelled": 2432456692
                      },
                      "percent": {
                        "vs_original": 90,
                        "vs_benchmark": 52
                      },
                      "benchmark_note": null,
                      "human": {
                        "original": "11.2 GB",
                        "optimised": "1.1 GB",
                        "saved": "10.1 GB"
                      },
                      "first_day": "2026-08-18",
                      "last_day": "2026-09-15"
                    }
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 42
                    },
                    "name": {
                      "type": "string",
                      "example": "Acme Digital"
                    },
                    "email": {
                      "type": "string",
                      "example": "ops@acme.example"
                    },
                    "kind": {
                      "type": "string",
                      "example": "agency"
                    },
                    "plan": {
                      "type": "string",
                      "example": "dive"
                    },
                    "status": {
                      "type": "string",
                      "example": "active"
                    },
                    "entitlement": {
                      "type": "object",
                      "properties": {
                        "sites_bought": {
                          "type": "integer",
                          "example": 25
                        },
                        "sites_in_use": {
                          "type": "integer",
                          "example": 11
                        },
                        "warm_locations": {
                          "type": "integer",
                          "example": 3
                        },
                        "shield": {
                          "type": "boolean",
                          "example": true
                        }
                      }
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "images": {
                          "type": "integer",
                          "example": 54874
                        },
                        "bytes": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "integer",
                              "example": 12020896087
                            },
                            "optimised": {
                              "type": "integer",
                              "example": 1169791846
                            },
                            "saved": {
                              "type": "integer",
                              "example": 10851104241
                            },
                            "benchmark_modelled": {
                              "type": "integer",
                              "example": 2432456692
                            }
                          }
                        },
                        "percent": {
                          "type": "object",
                          "properties": {
                            "vs_original": {
                              "type": "integer",
                              "example": 90
                            },
                            "vs_benchmark": {
                              "type": "integer",
                              "example": 52
                            }
                          }
                        },
                        "benchmark_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "human": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "string",
                              "example": "11.2 GB"
                            },
                            "optimised": {
                              "type": "string",
                              "example": "1.1 GB"
                            },
                            "saved": {
                              "type": "string",
                              "example": "10.1 GB"
                            }
                          }
                        },
                        "first_day": {
                          "type": "string",
                          "example": "2026-08-18"
                        },
                        "last_day": {
                          "type": "string",
                          "example": "2026-09-15"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Discovery"
        ]
      }
    },
    "/api/v1/sites": {
      "get": {
        "summary": "List sites.",
        "operationId": "listSites",
        "description": "Every site on the account with its delivery state, DNS state and lifetime\nbyte totals. The right first call for \"which of my sites has a problem\".",
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "description": "active or paused.",
            "example": "active",
            "required": false,
            "schema": {
              "type": "string",
              "description": "active or paused.",
              "example": "active"
            }
          },
          {
            "in": "query",
            "name": "delivery_state",
            "description": "ok, degraded, down or unknown.",
            "example": "degraded",
            "required": false,
            "schema": {
              "type": "string",
              "description": "ok, degraded, down or unknown.",
              "example": "degraded"
            }
          },
          {
            "in": "query",
            "name": "q",
            "description": "Domain or label substring.",
            "example": "breckland",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Domain or label substring.",
              "example": "breckland"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "example": 7
                      },
                      "domain": {
                        "type": "string",
                        "example": "example.co.uk"
                      },
                      "label": {
                        "type": "string",
                        "example": "example-co-uk"
                      },
                      "cdn_base": {
                        "type": "string",
                        "example": "https://images.example.co.uk"
                      },
                      "status": {
                        "type": "string",
                        "example": "active"
                      },
                      "delivery_state": {
                        "type": "string",
                        "example": "ok"
                      },
                      "dns_state": {
                        "type": "string",
                        "example": "ok"
                      },
                      "images": {
                        "type": "integer",
                        "example": 4223
                      },
                      "original_bytes": {
                        "type": "integer",
                        "example": 229983921
                      },
                      "optimised_bytes": {
                        "type": "integer",
                        "example": 37648511
                      },
                      "benchmark_bytes": {
                        "type": "integer",
                        "example": 54807310
                      }
                    }
                  },
                  "example": [
                    {
                      "id": 7,
                      "domain": "example.co.uk",
                      "label": "example-co-uk",
                      "cdn_base": "https://images.example.co.uk",
                      "status": "active",
                      "delivery_state": "ok",
                      "dns_state": "ok",
                      "images": 4223,
                      "original_bytes": 229983921,
                      "optimised_bytes": 37648511,
                      "benchmark_bytes": 54807310
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ]
      },
      "post": {
        "summary": "Register a site that already delivers.",
        "operationId": "registerASiteThatAlreadyDelivers",
        "description": "Writes one row and returns one key. Touches no CDN resource - that is the\nwhole point of it being separate from provisioning. The live estate's\npull zones already exist and pull from an origin rather than storage, so\nprovisioning one of them would build a new storage-backed zone and move\nthe hostname onto it: a full outage arrived at by running the tool that\nlooks right.\n\nRegistration claims a NAME. It proves nothing about who owns the domain,\nand nothing happens until the key is installed on the site and DNS points\nhere. A domain whose only claim is a deleted row is revived for its own\naccount and released for anybody else.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "site": {
                      "id": 8,
                      "label": "example-co-uk",
                      "domain": "example.co.uk",
                      "status": "active",
                      "created_at": "2026-09-15T08:20:00.000000Z",
                      "delivery": {
                        "cdn_base": null,
                        "storage_zone": null,
                        "path_prefix": "wp-content/uploads",
                        "state": "unknown",
                        "checked_at": null,
                        "ok_at": null,
                        "fails": 0
                      },
                      "dns": {
                        "state": "unknown",
                        "checked_at": null,
                        "ok_at": null,
                        "nudges": 0
                      },
                      "warm": {
                        "status": "paused",
                        "regions": [],
                        "interval_seconds": 604800,
                        "last_run_at": null,
                        "last_warmed": 0,
                        "last_errors": 0,
                        "last_note": null,
                        "queue_pending": 0,
                        "pops": []
                      },
                      "key": {
                        "prefix": "9f2c1ab4"
                      },
                      "report": {
                        "enabled": false,
                        "url": null
                      },
                      "usage": {
                        "images": 0,
                        "bytes": {
                          "original": 0,
                          "optimised": 0,
                          "saved": 0,
                          "benchmark_modelled": 0
                        },
                        "percent": {
                          "vs_original": 0,
                          "vs_benchmark": null
                        },
                        "benchmark_note": "No modelled benchmark recorded for this data.",
                        "human": {
                          "original": "0 B",
                          "optimised": "0 B",
                          "saved": "0 B"
                        },
                        "first_day": null,
                        "last_day": null
                      }
                    },
                    "key": "9f2c1ab4...shown once and never stored...",
                    "warning": "Shown once and never stored in plaintext. Paste it into the Peregrine plugin on example.co.uk now."
                  },
                  "properties": {
                    "site": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "example": 8
                        },
                        "label": {
                          "type": "string",
                          "example": "example-co-uk"
                        },
                        "domain": {
                          "type": "string",
                          "example": "example.co.uk"
                        },
                        "status": {
                          "type": "string",
                          "example": "active"
                        },
                        "created_at": {
                          "type": "string",
                          "example": "2026-09-15T08:20:00.000000Z"
                        },
                        "delivery": {
                          "type": "object",
                          "properties": {
                            "cdn_base": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "storage_zone": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "path_prefix": {
                              "type": "string",
                              "example": "wp-content/uploads"
                            },
                            "state": {
                              "type": "string",
                              "example": "unknown"
                            },
                            "checked_at": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "ok_at": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "fails": {
                              "type": "integer",
                              "example": 0
                            }
                          }
                        },
                        "dns": {
                          "type": "object",
                          "properties": {
                            "state": {
                              "type": "string",
                              "example": "unknown"
                            },
                            "checked_at": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "ok_at": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "nudges": {
                              "type": "integer",
                              "example": 0
                            }
                          }
                        },
                        "warm": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "example": "paused"
                            },
                            "regions": {
                              "type": "array",
                              "example": []
                            },
                            "interval_seconds": {
                              "type": "integer",
                              "example": 604800
                            },
                            "last_run_at": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "last_warmed": {
                              "type": "integer",
                              "example": 0
                            },
                            "last_errors": {
                              "type": "integer",
                              "example": 0
                            },
                            "last_note": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "queue_pending": {
                              "type": "integer",
                              "example": 0
                            },
                            "pops": {
                              "type": "array",
                              "example": []
                            }
                          }
                        },
                        "key": {
                          "type": "object",
                          "properties": {
                            "prefix": {
                              "type": "string",
                              "example": "9f2c1ab4"
                            }
                          }
                        },
                        "report": {
                          "type": "object",
                          "properties": {
                            "enabled": {
                              "type": "boolean",
                              "example": false
                            },
                            "url": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            }
                          }
                        },
                        "usage": {
                          "type": "object",
                          "properties": {
                            "images": {
                              "type": "integer",
                              "example": 0
                            },
                            "bytes": {
                              "type": "object",
                              "properties": {
                                "original": {
                                  "type": "integer",
                                  "example": 0
                                },
                                "optimised": {
                                  "type": "integer",
                                  "example": 0
                                },
                                "saved": {
                                  "type": "integer",
                                  "example": 0
                                },
                                "benchmark_modelled": {
                                  "type": "integer",
                                  "example": 0
                                }
                              }
                            },
                            "percent": {
                              "type": "object",
                              "properties": {
                                "vs_original": {
                                  "type": "integer",
                                  "example": 0
                                },
                                "vs_benchmark": {
                                  "type": "string",
                                  "example": null,
                                  "nullable": true
                                }
                              }
                            },
                            "benchmark_note": {
                              "type": "string",
                              "example": "No modelled benchmark recorded for this data."
                            },
                            "human": {
                              "type": "object",
                              "properties": {
                                "original": {
                                  "type": "string",
                                  "example": "0 B"
                                },
                                "optimised": {
                                  "type": "string",
                                  "example": "0 B"
                                },
                                "saved": {
                                  "type": "string",
                                  "example": "0 B"
                                }
                              }
                            },
                            "first_day": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            },
                            "last_day": {
                              "type": "string",
                              "example": null,
                              "nullable": true
                            }
                          }
                        }
                      }
                    },
                    "key": {
                      "type": "string",
                      "example": "9f2c1ab4...shown once and never stored..."
                    },
                    "warning": {
                      "type": "string",
                      "example": "Shown once and never stored in plaintext. Paste it into the Peregrine plugin on example.co.uk now."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "the token lacks the ability",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "missing_ability",
                    "ability": "sites",
                    "detail": "This token cannot create, rename, pause and resume sites; rotate site keys. Issue a token with the \"sites\" ability from the console at https://app.peregrinecdn.com/tokens, then retry."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "missing_ability"
                    },
                    "ability": {
                      "type": "string",
                      "example": "sites"
                    },
                    "detail": {
                      "type": "string",
                      "example": "This token cannot create, rename, pause and resume sites; rotate site keys. Issue a token with the \"sites\" ability from the console at https://app.peregrinecdn.com/tokens, then retry."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "the domain is already registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "site_exists",
                    "detail": "That domain or label is already in use.",
                    "site": null
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "site_exists"
                    },
                    "detail": {
                      "type": "string",
                      "example": "That domain or label is already in use."
                    },
                    "site": {
                      "type": "string",
                      "example": null,
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The site's domain.",
                    "example": "example.co.uk"
                  },
                  "label": {
                    "type": "string",
                    "description": "A slug. Defaults to the domain with dots replaced.",
                    "example": "example-co-uk"
                  },
                  "cdn_base": {
                    "type": "string",
                    "description": "Delivery hostname, optionally with a scheme. No port, path or bare IP.",
                    "example": "https://images.example.co.uk"
                  },
                  "path_prefix": {
                    "type": "string",
                    "description": "Defaults to wp-content/uploads.",
                    "example": "wp-content/uploads"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/sites/{site}": {
      "get": {
        "summary": "Everything about one site.",
        "operationId": "everythingAboutOneSite",
        "description": "Deliberately one call rather than five. The question an operator actually\nasks is \"is this site fine\", and the parts of the answer live in four\ntables - usage, delivery, DNS and warming. Making the caller fetch and\njoin them is how a check gets half done.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 7,
                    "label": "example-co-uk",
                    "domain": "example.co.uk",
                    "status": "active",
                    "created_at": "2026-08-18T09:12:44.000000Z",
                    "delivery": {
                      "cdn_base": "https://images.example.co.uk",
                      "storage_zone": "pg-example-co-uk",
                      "path_prefix": "wp-content/uploads",
                      "state": "ok",
                      "checked_at": "2026-09-15T07:40:11.000000Z",
                      "ok_at": "2026-09-15T07:40:11.000000Z",
                      "fails": 0
                    },
                    "dns": {
                      "state": "ok",
                      "checked_at": "2026-09-15T06:00:00.000000Z",
                      "ok_at": "2026-08-19T10:02:00.000000Z",
                      "nudges": 0
                    },
                    "warm": {
                      "status": "active",
                      "regions": [
                        "UK",
                        "DE"
                      ],
                      "interval_seconds": 604800,
                      "last_run_at": "2026-09-14T02:11:09.000000Z",
                      "last_warmed": 812,
                      "last_errors": 0,
                      "last_note": "purged+rewarmed 812 url(s) across 2 pop(s)",
                      "queue_pending": 0,
                      "pops": [
                        {
                          "hostname": "example.b-cdn.net",
                          "region": "UK",
                          "pop_code": "UK1-1486",
                          "edge_ip": "203.0.113.10",
                          "resolved_at": "2026-09-14T02:10:58.000000Z"
                        }
                      ]
                    },
                    "key": {
                      "prefix": "9f2c1ab4"
                    },
                    "report": {
                      "enabled": true,
                      "url": "https://app.peregrinecdn.com/r/4f1c8a2e9b7d6350a1c2e4f6b8d0a2c4"
                    },
                    "usage": {
                      "images": 4223,
                      "bytes": {
                        "original": 229983921,
                        "optimised": 37648511,
                        "saved": 192335410,
                        "benchmark_modelled": 54807310
                      },
                      "percent": {
                        "vs_original": 84,
                        "vs_benchmark": 31
                      },
                      "benchmark_note": null,
                      "human": {
                        "original": "219 MB",
                        "optimised": "35.9 MB",
                        "saved": "183 MB"
                      },
                      "first_day": "2026-08-18",
                      "last_day": "2026-09-15"
                    }
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 7
                    },
                    "label": {
                      "type": "string",
                      "example": "example-co-uk"
                    },
                    "domain": {
                      "type": "string",
                      "example": "example.co.uk"
                    },
                    "status": {
                      "type": "string",
                      "example": "active"
                    },
                    "created_at": {
                      "type": "string",
                      "example": "2026-08-18T09:12:44.000000Z"
                    },
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "cdn_base": {
                          "type": "string",
                          "example": "https://images.example.co.uk"
                        },
                        "storage_zone": {
                          "type": "string",
                          "example": "pg-example-co-uk"
                        },
                        "path_prefix": {
                          "type": "string",
                          "example": "wp-content/uploads"
                        },
                        "state": {
                          "type": "string",
                          "example": "ok"
                        },
                        "checked_at": {
                          "type": "string",
                          "example": "2026-09-15T07:40:11.000000Z"
                        },
                        "ok_at": {
                          "type": "string",
                          "example": "2026-09-15T07:40:11.000000Z"
                        },
                        "fails": {
                          "type": "integer",
                          "example": 0
                        }
                      }
                    },
                    "dns": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string",
                          "example": "ok"
                        },
                        "checked_at": {
                          "type": "string",
                          "example": "2026-09-15T06:00:00.000000Z"
                        },
                        "ok_at": {
                          "type": "string",
                          "example": "2026-08-19T10:02:00.000000Z"
                        },
                        "nudges": {
                          "type": "integer",
                          "example": 0
                        }
                      }
                    },
                    "warm": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "example": "active"
                        },
                        "regions": {
                          "type": "array",
                          "example": [
                            "UK",
                            "DE"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "interval_seconds": {
                          "type": "integer",
                          "example": 604800
                        },
                        "last_run_at": {
                          "type": "string",
                          "example": "2026-09-14T02:11:09.000000Z"
                        },
                        "last_warmed": {
                          "type": "integer",
                          "example": 812
                        },
                        "last_errors": {
                          "type": "integer",
                          "example": 0
                        },
                        "last_note": {
                          "type": "string",
                          "example": "purged+rewarmed 812 url(s) across 2 pop(s)"
                        },
                        "queue_pending": {
                          "type": "integer",
                          "example": 0
                        },
                        "pops": {
                          "type": "array",
                          "example": [
                            {
                              "hostname": "example.b-cdn.net",
                              "region": "UK",
                              "pop_code": "UK1-1486",
                              "edge_ip": "203.0.113.10",
                              "resolved_at": "2026-09-14T02:10:58.000000Z"
                            }
                          ],
                          "items": {
                            "type": "object",
                            "properties": {
                              "hostname": {
                                "type": "string",
                                "example": "example.b-cdn.net"
                              },
                              "region": {
                                "type": "string",
                                "example": "UK"
                              },
                              "pop_code": {
                                "type": "string",
                                "example": "UK1-1486"
                              },
                              "edge_ip": {
                                "type": "string",
                                "example": "203.0.113.10"
                              },
                              "resolved_at": {
                                "type": "string",
                                "example": "2026-09-14T02:10:58.000000Z"
                              }
                            }
                          }
                        }
                      }
                    },
                    "key": {
                      "type": "object",
                      "properties": {
                        "prefix": {
                          "type": "string",
                          "example": "9f2c1ab4"
                        }
                      }
                    },
                    "report": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "example": true
                        },
                        "url": {
                          "type": "string",
                          "example": "https://app.peregrinecdn.com/r/4f1c8a2e9b7d6350a1c2e4f6b8d0a2c4"
                        }
                      }
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "images": {
                          "type": "integer",
                          "example": 4223
                        },
                        "bytes": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "integer",
                              "example": 229983921
                            },
                            "optimised": {
                              "type": "integer",
                              "example": 37648511
                            },
                            "saved": {
                              "type": "integer",
                              "example": 192335410
                            },
                            "benchmark_modelled": {
                              "type": "integer",
                              "example": 54807310
                            }
                          }
                        },
                        "percent": {
                          "type": "object",
                          "properties": {
                            "vs_original": {
                              "type": "integer",
                              "example": 84
                            },
                            "vs_benchmark": {
                              "type": "integer",
                              "example": 31
                            }
                          }
                        },
                        "benchmark_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "human": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "string",
                              "example": "219 MB"
                            },
                            "optimised": {
                              "type": "string",
                              "example": "35.9 MB"
                            },
                            "saved": {
                              "type": "string",
                              "example": "183 MB"
                            }
                          }
                        },
                        "first_day": {
                          "type": "string",
                          "example": "2026-08-18"
                        },
                        "last_day": {
                          "type": "string",
                          "example": "2026-09-15"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "no site on this account matches",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "site_not_found",
                    "ref": "exampl.co.uk",
                    "known": [
                      "example.co.uk",
                      "shop.example.co.uk"
                    ],
                    "detail": "No site on this account matches that id, label or domain. The \"known\" list is what does exist."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "site_not_found"
                    },
                    "ref": {
                      "type": "string",
                      "example": "exampl.co.uk"
                    },
                    "known": {
                      "type": "array",
                      "example": [
                        "example.co.uk",
                        "shop.example.co.uk"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "detail": {
                      "type": "string",
                      "example": "No site on this account matches that id, label or domain. The \"known\" list is what does exist."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ]
      },
      "patch": {
        "summary": "Rename, repoint, pause or resume.",
        "operationId": "renameRepointPauseOrResume",
        "description": "Pausing is a real operation rather than a UI state: a paused site stops\nbeing warmed and stops counting against the site allowance, so it is the\nhonest thing to do with a client who has left.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 7,
                    "label": "example-co-uk",
                    "domain": "example.co.uk",
                    "status": "paused",
                    "created_at": "2026-08-18T09:12:44.000000Z",
                    "delivery": {
                      "cdn_base": "https://images.example.co.uk",
                      "storage_zone": "pg-example-co-uk",
                      "path_prefix": "wp-content/uploads",
                      "state": "ok",
                      "checked_at": "2026-09-15T07:40:11.000000Z",
                      "ok_at": "2026-09-15T07:40:11.000000Z",
                      "fails": 0
                    },
                    "dns": {
                      "state": "ok",
                      "checked_at": null,
                      "ok_at": null,
                      "nudges": 0
                    },
                    "warm": {
                      "status": "active",
                      "regions": [
                        "UK"
                      ],
                      "interval_seconds": 604800,
                      "last_run_at": null,
                      "last_warmed": 0,
                      "last_errors": 0,
                      "last_note": null,
                      "queue_pending": 0,
                      "pops": []
                    },
                    "key": {
                      "prefix": "9f2c1ab4"
                    },
                    "report": {
                      "enabled": false,
                      "url": null
                    },
                    "usage": {
                      "images": 4223,
                      "bytes": {
                        "original": 229983921,
                        "optimised": 37648511,
                        "saved": 192335410,
                        "benchmark_modelled": 54807310
                      },
                      "percent": {
                        "vs_original": 84,
                        "vs_benchmark": 31
                      },
                      "benchmark_note": null,
                      "human": {
                        "original": "219 MB",
                        "optimised": "35.9 MB",
                        "saved": "183 MB"
                      },
                      "first_day": "2026-08-18",
                      "last_day": "2026-09-15"
                    }
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 7
                    },
                    "label": {
                      "type": "string",
                      "example": "example-co-uk"
                    },
                    "domain": {
                      "type": "string",
                      "example": "example.co.uk"
                    },
                    "status": {
                      "type": "string",
                      "example": "paused"
                    },
                    "created_at": {
                      "type": "string",
                      "example": "2026-08-18T09:12:44.000000Z"
                    },
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "cdn_base": {
                          "type": "string",
                          "example": "https://images.example.co.uk"
                        },
                        "storage_zone": {
                          "type": "string",
                          "example": "pg-example-co-uk"
                        },
                        "path_prefix": {
                          "type": "string",
                          "example": "wp-content/uploads"
                        },
                        "state": {
                          "type": "string",
                          "example": "ok"
                        },
                        "checked_at": {
                          "type": "string",
                          "example": "2026-09-15T07:40:11.000000Z"
                        },
                        "ok_at": {
                          "type": "string",
                          "example": "2026-09-15T07:40:11.000000Z"
                        },
                        "fails": {
                          "type": "integer",
                          "example": 0
                        }
                      }
                    },
                    "dns": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string",
                          "example": "ok"
                        },
                        "checked_at": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "ok_at": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "nudges": {
                          "type": "integer",
                          "example": 0
                        }
                      }
                    },
                    "warm": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "example": "active"
                        },
                        "regions": {
                          "type": "array",
                          "example": [
                            "UK"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "interval_seconds": {
                          "type": "integer",
                          "example": 604800
                        },
                        "last_run_at": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "last_warmed": {
                          "type": "integer",
                          "example": 0
                        },
                        "last_errors": {
                          "type": "integer",
                          "example": 0
                        },
                        "last_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "queue_pending": {
                          "type": "integer",
                          "example": 0
                        },
                        "pops": {
                          "type": "array",
                          "example": []
                        }
                      }
                    },
                    "key": {
                      "type": "object",
                      "properties": {
                        "prefix": {
                          "type": "string",
                          "example": "9f2c1ab4"
                        }
                      }
                    },
                    "report": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "example": false
                        },
                        "url": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        }
                      }
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "images": {
                          "type": "integer",
                          "example": 4223
                        },
                        "bytes": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "integer",
                              "example": 229983921
                            },
                            "optimised": {
                              "type": "integer",
                              "example": 37648511
                            },
                            "saved": {
                              "type": "integer",
                              "example": 192335410
                            },
                            "benchmark_modelled": {
                              "type": "integer",
                              "example": 54807310
                            }
                          }
                        },
                        "percent": {
                          "type": "object",
                          "properties": {
                            "vs_original": {
                              "type": "integer",
                              "example": 84
                            },
                            "vs_benchmark": {
                              "type": "integer",
                              "example": 31
                            }
                          }
                        },
                        "benchmark_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "human": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "string",
                              "example": "219 MB"
                            },
                            "optimised": {
                              "type": "string",
                              "example": "35.9 MB"
                            },
                            "saved": {
                              "type": "string",
                              "example": "183 MB"
                            }
                          }
                        },
                        "first_day": {
                          "type": "string",
                          "example": "2026-08-18"
                        },
                        "last_day": {
                          "type": "string",
                          "example": "2026-09-15"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "cdn_base was not a bare hostname",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "message": "The cdn base field format is invalid.",
                    "errors": {
                      "cdn_base": [
                        "The cdn base field format is invalid."
                      ]
                    }
                  },
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "The cdn base field format is invalid."
                    },
                    "errors": {
                      "type": "object",
                      "properties": {
                        "cdn_base": {
                          "type": "array",
                          "example": [
                            "The cdn base field format is invalid."
                          ],
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "description": "A new slug.",
                    "example": "example-co-uk"
                  },
                  "status": {
                    "type": "string",
                    "description": "active or paused.",
                    "example": "paused"
                  },
                  "cdn_base": {
                    "type": "string",
                    "description": "Delivery hostname.",
                    "example": "https://images.example.co.uk"
                  },
                  "path_prefix": {
                    "type": "string",
                    "description": "",
                    "example": "wp-content/uploads"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Stop managing a site.",
        "operationId": "stopManagingASite",
        "description": "Soft. Usage history is kept, because it is the evidence behind every\nsavings figure already quoted to that client. The CDN zones are\ndeliberately not touched - deleting a pull zone from an API call is how a\nlive site goes dark on a typo.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "deleted": true,
                    "detail": "Stopped managing example.co.uk. Usage history is kept. Its Bunny zones were not touched - remove them in Bunny if you want them gone."
                  },
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "example": true
                    },
                    "detail": {
                      "type": "string",
                      "example": "Stopped managing example.co.uk. Usage history is kept. Its Bunny zones were not touched - remove them in Bunny if you want them gone."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/key": {
      "post": {
        "summary": "Rotate the site key.",
        "operationId": "rotateTheSiteKey",
        "description": "The old key stops working the moment this returns, so the plugin on that\nsite is disconnected until the new one is pasted in. Do not do this\nspeculatively. Plaintext is returned once and never stored.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "key": "9f2c1ab4...shown once and never stored...",
                    "prefix": "9f2c1ab4",
                    "warning": "Shown once. The old key stopped working the moment this returned, so the plugin on example.co.uk is disconnected until this is pasted in."
                  },
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "9f2c1ab4...shown once and never stored..."
                    },
                    "prefix": {
                      "type": "string",
                      "example": "9f2c1ab4"
                    },
                    "warning": {
                      "type": "string",
                      "example": "Shown once. The old key stopped working the moment this returned, so the plugin on example.co.uk is disconnected until this is pasted in."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/images": {
      "get": {
        "summary": "What is still heavy.",
        "operationId": "whatIsStillHeavy",
        "description": "Two lists, because they need different fixes. \"heaviest\" is the largest\nfiles a visitor still downloads. \"poorly_compressed\" barely shrank -\nalmost always a photograph saved as a PNG, or something already\ncompressed. No encoder setting fixes the second list; the source file has\nto change.\n\nFigures are the AVIF rows only, which is what a visitor receives.",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "description": "How many in each list, 1-100. Defaults to 20.",
            "example": 20,
            "required": false,
            "schema": {
              "type": "integer",
              "description": "How many in each list, 1-100. Defaults to 20.",
              "example": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "site_id": 7,
                    "domain": "example.co.uk",
                    "format": "avif",
                    "note": "Figures are the AVIF rows only - what a visitor downloads. WebP fallbacks cover the same originals and are not added in.",
                    "heaviest": [
                      {
                        "path": "/wp-content/uploads/2026/03/team-photo.png",
                        "original_bytes": 412880,
                        "optimised_bytes": 380114,
                        "quality": 45
                      }
                    ],
                    "poorly_compressed": [
                      {
                        "path": "/wp-content/uploads/2026/03/team-photo.png",
                        "original_bytes": 412880,
                        "optimised_bytes": 380114,
                        "quality": 45
                      }
                    ],
                    "unpathed": 0
                  },
                  "properties": {
                    "site_id": {
                      "type": "integer",
                      "example": 7
                    },
                    "domain": {
                      "type": "string",
                      "example": "example.co.uk"
                    },
                    "format": {
                      "type": "string",
                      "example": "avif"
                    },
                    "note": {
                      "type": "string",
                      "example": "Figures are the AVIF rows only - what a visitor downloads. WebP fallbacks cover the same originals and are not added in."
                    },
                    "heaviest": {
                      "type": "array",
                      "example": [
                        {
                          "path": "/wp-content/uploads/2026/03/team-photo.png",
                          "original_bytes": 412880,
                          "optimised_bytes": 380114,
                          "quality": 45
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string",
                            "example": "/wp-content/uploads/2026/03/team-photo.png"
                          },
                          "original_bytes": {
                            "type": "integer",
                            "example": 412880
                          },
                          "optimised_bytes": {
                            "type": "integer",
                            "example": 380114
                          },
                          "quality": {
                            "type": "integer",
                            "example": 45
                          }
                        }
                      }
                    },
                    "poorly_compressed": {
                      "type": "array",
                      "example": [
                        {
                          "path": "/wp-content/uploads/2026/03/team-photo.png",
                          "original_bytes": 412880,
                          "optimised_bytes": 380114,
                          "quality": 45
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string",
                            "example": "/wp-content/uploads/2026/03/team-photo.png"
                          },
                          "original_bytes": {
                            "type": "integer",
                            "example": 412880
                          },
                          "optimised_bytes": {
                            "type": "integer",
                            "example": 380114
                          },
                          "quality": {
                            "type": "integer",
                            "example": 45
                          }
                        }
                      }
                    },
                    "unpathed": {
                      "type": "integer",
                      "example": 0
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/verify": {
      "post": {
        "summary": "Prove a visitor receives optimised images.",
        "operationId": "proveAVisitorReceivesOptimisedImages",
        "description": "Fetches the live page, finds an asset on the delivery hostname, and\nfetches that too.\n\nEvery failure this product has had arrived as a silent success: the\nplugin reports connected, the site returns 200, the conversions table\nfills up, and not one optimised byte reaches a browser - because a DNS\nrecord changed, a certificate lapsed, or a key was rotated and never\npasted back. Nothing on the WordPress side can see that; the evidence\nexists only from outside.\n\n`stage` walks the chain: page, markup, format, asset, delivered. Both\nhostnames are checked to resolve only to public addresses, and the\nconnection is pinned to the address that passed.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "description": "delivering",
                      "type": "object",
                      "example": {
                        "site_id": 7,
                        "domain": "example.co.uk",
                        "ok": true,
                        "stage": "delivered",
                        "references": 42,
                        "sample": "https://images.example.co.uk/wp-content/uploads/2026/03/hero.jpg.avif",
                        "status": 200,
                        "content_type": "image/avif",
                        "cache": "HIT",
                        "why": "A visitor receives image/avif from images.example.co.uk."
                      },
                      "properties": {
                        "site_id": {
                          "type": "integer",
                          "example": 7
                        },
                        "domain": {
                          "type": "string",
                          "example": "example.co.uk"
                        },
                        "ok": {
                          "type": "boolean",
                          "example": true
                        },
                        "stage": {
                          "type": "string",
                          "example": "delivered"
                        },
                        "references": {
                          "type": "integer",
                          "example": 42
                        },
                        "sample": {
                          "type": "string",
                          "example": "https://images.example.co.uk/wp-content/uploads/2026/03/hero.jpg.avif"
                        },
                        "status": {
                          "type": "integer",
                          "example": 200
                        },
                        "content_type": {
                          "type": "string",
                          "example": "image/avif"
                        },
                        "cache": {
                          "type": "string",
                          "example": "HIT"
                        },
                        "why": {
                          "type": "string",
                          "example": "A visitor receives image/avif from images.example.co.uk."
                        }
                      }
                    },
                    {
                      "description": "not delivering",
                      "type": "object",
                      "example": {
                        "site_id": 7,
                        "domain": "example.co.uk",
                        "ok": false,
                        "stage": "format",
                        "references": 42,
                        "sample": null,
                        "status": 0,
                        "content_type": "",
                        "cache": null,
                        "why": "The page references images.example.co.uk but serves no AVIF or WebP from it, so visitors are still downloading originals."
                      },
                      "properties": {
                        "site_id": {
                          "type": "integer",
                          "example": 7
                        },
                        "domain": {
                          "type": "string",
                          "example": "example.co.uk"
                        },
                        "ok": {
                          "type": "boolean",
                          "example": false
                        },
                        "stage": {
                          "type": "string",
                          "example": "format"
                        },
                        "references": {
                          "type": "integer",
                          "example": 42
                        },
                        "sample": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "status": {
                          "type": "integer",
                          "example": 0
                        },
                        "content_type": {
                          "type": "string",
                          "example": ""
                        },
                        "cache": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "why": {
                          "type": "string",
                          "example": "The page references images.example.co.uk but serves no AVIF or WebP from it, so visitors are still downloading originals."
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/report": {
      "post": {
        "summary": "Turn the client report link on or off.",
        "operationId": "turnTheClientReportLinkOnOrOff",
        "description": "Disabling clears the token rather than flipping a flag, so a link that\nhas been revoked cannot come back later by someone toggling the flag the\nother way.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "description": "enabling",
                      "type": "object",
                      "example": {
                        "enabled": true,
                        "url": "https://app.peregrinecdn.com/r/4f1c8a2e9b7d6350a1c2e4f6b8d0a2c4",
                        "email": "client@example.co.uk"
                      },
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "example": true
                        },
                        "url": {
                          "type": "string",
                          "example": "https://app.peregrinecdn.com/r/4f1c8a2e9b7d6350a1c2e4f6b8d0a2c4"
                        },
                        "email": {
                          "type": "string",
                          "example": "client@example.co.uk"
                        }
                      }
                    },
                    {
                      "description": "disabling - the token is destroyed, not hidden",
                      "type": "object",
                      "example": {
                        "enabled": false,
                        "url": null
                      },
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "example": false
                        },
                        "url": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Sites"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Whether the shareable report link exists.",
                    "example": true
                  },
                  "email": {
                    "type": "string",
                    "description": "Address the monthly report is posted to.",
                    "example": "client@example.co.uk"
                  }
                },
                "required": [
                  "enabled"
                ]
              }
            }
          }
        }
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/stats": {
      "get": {
        "summary": "Lifetime totals for the account.",
        "operationId": "lifetimeTotalsForTheAccount",
        "description": "Reconciled server-side. Do not re-derive savings by summing columns: the\nrollup is keyed by format and holds every image twice.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "images": 54874,
                    "bytes": {
                      "original": 12020896087,
                      "optimised": 1169791846,
                      "saved": 10851104241,
                      "benchmark_modelled": 2432456692
                    },
                    "percent": {
                      "vs_original": 90,
                      "vs_benchmark": 52
                    },
                    "benchmark_note": null,
                    "human": {
                      "original": "11.2 GB",
                      "optimised": "1.1 GB",
                      "saved": "10.1 GB"
                    },
                    "first_day": "2026-08-18",
                    "last_day": "2026-09-15",
                    "seconds_saved_at_10mbps": 8680.9
                  },
                  "properties": {
                    "images": {
                      "type": "integer",
                      "example": 54874
                    },
                    "bytes": {
                      "type": "object",
                      "properties": {
                        "original": {
                          "type": "integer",
                          "example": 12020896087
                        },
                        "optimised": {
                          "type": "integer",
                          "example": 1169791846
                        },
                        "saved": {
                          "type": "integer",
                          "example": 10851104241
                        },
                        "benchmark_modelled": {
                          "type": "integer",
                          "example": 2432456692
                        }
                      }
                    },
                    "percent": {
                      "type": "object",
                      "properties": {
                        "vs_original": {
                          "type": "integer",
                          "example": 90
                        },
                        "vs_benchmark": {
                          "type": "integer",
                          "example": 52
                        }
                      }
                    },
                    "benchmark_note": {
                      "type": "string",
                      "example": null,
                      "nullable": true
                    },
                    "human": {
                      "type": "object",
                      "properties": {
                        "original": {
                          "type": "string",
                          "example": "11.2 GB"
                        },
                        "optimised": {
                          "type": "string",
                          "example": "1.1 GB"
                        },
                        "saved": {
                          "type": "string",
                          "example": "10.1 GB"
                        }
                      }
                    },
                    "first_day": {
                      "type": "string",
                      "example": "2026-08-18"
                    },
                    "last_day": {
                      "type": "string",
                      "example": "2026-09-15"
                    },
                    "seconds_saved_at_10mbps": {
                      "type": "number",
                      "example": 8680.9
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Usage"
        ]
      }
    },
    "/api/v1/trend": {
      "get": {
        "summary": "Daily series across the account.",
        "operationId": "dailySeriesAcrossTheAccount",
        "description": "One row per day, already deduplicated by format.",
        "parameters": [
          {
            "in": "query",
            "name": "days",
            "description": "Days back to include, 1-365. Defaults to 30.",
            "example": 30,
            "required": false,
            "schema": {
              "type": "integer",
              "description": "Days back to include, 1-365. Defaults to 30.",
              "example": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "day": {
                        "type": "string",
                        "example": "2026-09-14"
                      },
                      "images": {
                        "type": "integer",
                        "example": 81
                      },
                      "original_bytes": {
                        "type": "integer",
                        "example": 19828439
                      },
                      "optimised_bytes": {
                        "type": "integer",
                        "example": 1720154
                      },
                      "saved": {
                        "type": "integer",
                        "example": 18108285
                      }
                    }
                  },
                  "example": [
                    {
                      "day": "2026-09-14",
                      "images": 81,
                      "original_bytes": 19828439,
                      "optimised_bytes": 1720154,
                      "saved": 18108285
                    },
                    {
                      "day": "2026-09-15",
                      "images": 183,
                      "original_bytes": 24211808,
                      "optimised_bytes": 5494995,
                      "saved": 18716813
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Usage"
        ]
      }
    },
    "/api/v1/usage": {
      "get": {
        "summary": "Usage over an explicit window.",
        "operationId": "usageOverAnExplicitWindow",
        "description": "What an invoice or a monthly client report is built from. The window is\ninclusive at both ends and echoed back, because a report that silently\ndropped its last day would be wrong in the direction nobody checks.",
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "description": "date Start of the window, inclusive, YYYY-MM-DD. Defaults to 30 days ago.",
            "example": "2026-08-01",
            "required": false,
            "schema": {
              "type": "string",
              "description": "date Start of the window, inclusive, YYYY-MM-DD. Defaults to 30 days ago.",
              "example": "2026-08-01"
            }
          },
          {
            "in": "query",
            "name": "to",
            "description": "date End of the window, inclusive, YYYY-MM-DD. Defaults to today.",
            "example": "2026-08-31",
            "required": false,
            "schema": {
              "type": "string",
              "description": "date End of the window, inclusive, YYYY-MM-DD. Defaults to today.",
              "example": "2026-08-31"
            }
          },
          {
            "in": "query",
            "name": "site",
            "description": "Restrict to one site.",
            "example": "example.co.uk",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Restrict to one site.",
              "example": "example.co.uk"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "from": "2026-08-01",
                    "to": "2026-08-31",
                    "total": {
                      "images": 4223,
                      "bytes": {
                        "original": 229983921,
                        "optimised": 37648511,
                        "saved": 192335410,
                        "benchmark_modelled": 54807310
                      },
                      "percent": {
                        "vs_original": 84,
                        "vs_benchmark": 31
                      },
                      "benchmark_note": null,
                      "human": {
                        "original": "219 MB",
                        "optimised": "35.9 MB",
                        "saved": "183 MB"
                      },
                      "first_day": "2026-08-18",
                      "last_day": "2026-08-31"
                    },
                    "sites": [
                      {
                        "site_id": 7,
                        "domain": "example.co.uk",
                        "label": "example-co-uk",
                        "images": 4223,
                        "bytes": {
                          "original": 229983921,
                          "optimised": 37648511,
                          "saved": 192335410,
                          "benchmark_modelled": 54807310
                        },
                        "percent": {
                          "vs_original": 84,
                          "vs_benchmark": 31
                        },
                        "benchmark_note": null,
                        "human": {
                          "original": "219 MB",
                          "optimised": "35.9 MB",
                          "saved": "183 MB"
                        },
                        "first_day": null,
                        "last_day": null
                      }
                    ]
                  },
                  "properties": {
                    "from": {
                      "type": "string",
                      "example": "2026-08-01"
                    },
                    "to": {
                      "type": "string",
                      "example": "2026-08-31"
                    },
                    "total": {
                      "type": "object",
                      "properties": {
                        "images": {
                          "type": "integer",
                          "example": 4223
                        },
                        "bytes": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "integer",
                              "example": 229983921
                            },
                            "optimised": {
                              "type": "integer",
                              "example": 37648511
                            },
                            "saved": {
                              "type": "integer",
                              "example": 192335410
                            },
                            "benchmark_modelled": {
                              "type": "integer",
                              "example": 54807310
                            }
                          }
                        },
                        "percent": {
                          "type": "object",
                          "properties": {
                            "vs_original": {
                              "type": "integer",
                              "example": 84
                            },
                            "vs_benchmark": {
                              "type": "integer",
                              "example": 31
                            }
                          }
                        },
                        "benchmark_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "human": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "string",
                              "example": "219 MB"
                            },
                            "optimised": {
                              "type": "string",
                              "example": "35.9 MB"
                            },
                            "saved": {
                              "type": "string",
                              "example": "183 MB"
                            }
                          }
                        },
                        "first_day": {
                          "type": "string",
                          "example": "2026-08-18"
                        },
                        "last_day": {
                          "type": "string",
                          "example": "2026-08-31"
                        }
                      }
                    },
                    "sites": {
                      "type": "array",
                      "example": [
                        {
                          "site_id": 7,
                          "domain": "example.co.uk",
                          "label": "example-co-uk",
                          "images": 4223,
                          "bytes": {
                            "original": 229983921,
                            "optimised": 37648511,
                            "saved": 192335410,
                            "benchmark_modelled": 54807310
                          },
                          "percent": {
                            "vs_original": 84,
                            "vs_benchmark": 31
                          },
                          "benchmark_note": null,
                          "human": {
                            "original": "219 MB",
                            "optimised": "35.9 MB",
                            "saved": "183 MB"
                          },
                          "first_day": null,
                          "last_day": null
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "site_id": {
                            "type": "integer",
                            "example": 7
                          },
                          "domain": {
                            "type": "string",
                            "example": "example.co.uk"
                          },
                          "label": {
                            "type": "string",
                            "example": "example-co-uk"
                          },
                          "images": {
                            "type": "integer",
                            "example": 4223
                          },
                          "bytes": {
                            "type": "object",
                            "properties": {
                              "original": {
                                "type": "integer",
                                "example": 229983921
                              },
                              "optimised": {
                                "type": "integer",
                                "example": 37648511
                              },
                              "saved": {
                                "type": "integer",
                                "example": 192335410
                              },
                              "benchmark_modelled": {
                                "type": "integer",
                                "example": 54807310
                              }
                            }
                          },
                          "percent": {
                            "type": "object",
                            "properties": {
                              "vs_original": {
                                "type": "integer",
                                "example": 84
                              },
                              "vs_benchmark": {
                                "type": "integer",
                                "example": 31
                              }
                            }
                          },
                          "benchmark_note": {
                            "type": "string",
                            "example": null,
                            "nullable": true
                          },
                          "human": {
                            "type": "object",
                            "properties": {
                              "original": {
                                "type": "string",
                                "example": "219 MB"
                              },
                              "optimised": {
                                "type": "string",
                                "example": "35.9 MB"
                              },
                              "saved": {
                                "type": "string",
                                "example": "183 MB"
                              }
                            }
                          },
                          "first_day": {
                            "type": "string",
                            "example": null,
                            "nullable": true
                          },
                          "last_day": {
                            "type": "string",
                            "example": null,
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Usage"
        ]
      }
    },
    "/api/v1/sites/{site}/usage": {
      "get": {
        "summary": "Usage for one site.",
        "operationId": "usageForOneSite",
        "description": "Lifetime and windowed, plus the daily series.",
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "description": "date Inclusive, YYYY-MM-DD.",
            "example": "2026-08-01",
            "required": false,
            "schema": {
              "type": "string",
              "description": "date Inclusive, YYYY-MM-DD.",
              "example": "2026-08-01"
            }
          },
          {
            "in": "query",
            "name": "to",
            "description": "date Inclusive, YYYY-MM-DD.",
            "example": "2026-08-31",
            "required": false,
            "schema": {
              "type": "string",
              "description": "date Inclusive, YYYY-MM-DD.",
              "example": "2026-08-31"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "site_id": 7,
                    "domain": "example.co.uk",
                    "from": "2026-08-16",
                    "to": "2026-09-15",
                    "lifetime": {
                      "images": 4223,
                      "bytes": {
                        "original": 229983921,
                        "optimised": 37648511,
                        "saved": 192335410,
                        "benchmark_modelled": 54807310
                      },
                      "percent": {
                        "vs_original": 84,
                        "vs_benchmark": 31
                      },
                      "benchmark_note": null,
                      "human": {
                        "original": "219 MB",
                        "optimised": "35.9 MB",
                        "saved": "183 MB"
                      },
                      "first_day": "2026-08-18",
                      "last_day": "2026-09-15"
                    },
                    "window": {
                      "images": 792,
                      "bytes": {
                        "original": 12939761,
                        "optimised": 3547457,
                        "saved": 9392304,
                        "benchmark_modelled": 6212516
                      },
                      "percent": {
                        "vs_original": 73,
                        "vs_benchmark": 43
                      },
                      "benchmark_note": null,
                      "human": {
                        "original": "12.3 MB",
                        "optimised": "3.4 MB",
                        "saved": "9.0 MB"
                      },
                      "first_day": "2026-08-19",
                      "last_day": "2026-08-19"
                    },
                    "days": [
                      {
                        "day": "2026-08-19",
                        "format": "avif",
                        "images": 792,
                        "original_bytes": 12939761,
                        "optimised_bytes": 3547457
                      }
                    ]
                  },
                  "properties": {
                    "site_id": {
                      "type": "integer",
                      "example": 7
                    },
                    "domain": {
                      "type": "string",
                      "example": "example.co.uk"
                    },
                    "from": {
                      "type": "string",
                      "example": "2026-08-16"
                    },
                    "to": {
                      "type": "string",
                      "example": "2026-09-15"
                    },
                    "lifetime": {
                      "type": "object",
                      "properties": {
                        "images": {
                          "type": "integer",
                          "example": 4223
                        },
                        "bytes": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "integer",
                              "example": 229983921
                            },
                            "optimised": {
                              "type": "integer",
                              "example": 37648511
                            },
                            "saved": {
                              "type": "integer",
                              "example": 192335410
                            },
                            "benchmark_modelled": {
                              "type": "integer",
                              "example": 54807310
                            }
                          }
                        },
                        "percent": {
                          "type": "object",
                          "properties": {
                            "vs_original": {
                              "type": "integer",
                              "example": 84
                            },
                            "vs_benchmark": {
                              "type": "integer",
                              "example": 31
                            }
                          }
                        },
                        "benchmark_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "human": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "string",
                              "example": "219 MB"
                            },
                            "optimised": {
                              "type": "string",
                              "example": "35.9 MB"
                            },
                            "saved": {
                              "type": "string",
                              "example": "183 MB"
                            }
                          }
                        },
                        "first_day": {
                          "type": "string",
                          "example": "2026-08-18"
                        },
                        "last_day": {
                          "type": "string",
                          "example": "2026-09-15"
                        }
                      }
                    },
                    "window": {
                      "type": "object",
                      "properties": {
                        "images": {
                          "type": "integer",
                          "example": 792
                        },
                        "bytes": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "integer",
                              "example": 12939761
                            },
                            "optimised": {
                              "type": "integer",
                              "example": 3547457
                            },
                            "saved": {
                              "type": "integer",
                              "example": 9392304
                            },
                            "benchmark_modelled": {
                              "type": "integer",
                              "example": 6212516
                            }
                          }
                        },
                        "percent": {
                          "type": "object",
                          "properties": {
                            "vs_original": {
                              "type": "integer",
                              "example": 73
                            },
                            "vs_benchmark": {
                              "type": "integer",
                              "example": 43
                            }
                          }
                        },
                        "benchmark_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "human": {
                          "type": "object",
                          "properties": {
                            "original": {
                              "type": "string",
                              "example": "12.3 MB"
                            },
                            "optimised": {
                              "type": "string",
                              "example": "3.4 MB"
                            },
                            "saved": {
                              "type": "string",
                              "example": "9.0 MB"
                            }
                          }
                        },
                        "first_day": {
                          "type": "string",
                          "example": "2026-08-19"
                        },
                        "last_day": {
                          "type": "string",
                          "example": "2026-08-19"
                        }
                      }
                    },
                    "days": {
                      "type": "array",
                      "example": [
                        {
                          "day": "2026-08-19",
                          "format": "avif",
                          "images": 792,
                          "original_bytes": 12939761,
                          "optimised_bytes": 3547457
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "day": {
                            "type": "string",
                            "example": "2026-08-19"
                          },
                          "format": {
                            "type": "string",
                            "example": "avif"
                          },
                          "images": {
                            "type": "integer",
                            "example": 792
                          },
                          "original_bytes": {
                            "type": "integer",
                            "example": 12939761
                          },
                          "optimised_bytes": {
                            "type": "integer",
                            "example": 3547457
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Usage"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/invalidate": {
      "post": {
        "summary": "Purge URLs from the edge.",
        "operationId": "purgeURLsFromTheEdge",
        "description": "The plugin never holds a CDN key - that rule is why this exists. It says\nwhat changed; the worker purges, which clears every PoP in one call, and\nre-warms at each PoP the site pays for.\n\nRejections come back in `rejected` rather than being dropped: a\n`{\"queued\": 0}` with a 200 is what weeks of silently-skipped purges look\nlike. More than 500 URLs is refused, not truncated.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "description": "",
                      "type": "object",
                      "example": {
                        "queued": 2,
                        "rejected": []
                      },
                      "properties": {
                        "queued": {
                          "type": "integer",
                          "example": 2
                        },
                        "rejected": {
                          "type": "array",
                          "example": []
                        }
                      }
                    },
                    {
                      "description": "some URLs were rejected - always read this, a queued of 0 with a 200 is what weeks of silently-skipped purges look like",
                      "type": "object",
                      "example": {
                        "queued": 1,
                        "rejected": [
                          {
                            "url": "not a url",
                            "why": "not a URL"
                          }
                        ]
                      },
                      "properties": {
                        "queued": {
                          "type": "integer",
                          "example": 1
                        },
                        "rejected": {
                          "type": "array",
                          "example": [
                            {
                              "url": "not a url",
                              "why": "not a URL"
                            }
                          ],
                          "items": {
                            "type": "object",
                            "properties": {
                              "url": {
                                "type": "string",
                                "example": "not a url"
                              },
                              "why": {
                                "type": "string",
                                "example": "not a URL"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "the queue is already full",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "queue_full",
                    "pending": 20000,
                    "detail": "This site already has 20000 URLs queued and not yet purged. Nothing was added."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "queue_full"
                    },
                    "pending": {
                      "type": "integer",
                      "example": 20000
                    },
                    "detail": {
                      "type": "string",
                      "example": "This site already has 20000 URLs queued and not yet purged. Nothing was added."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Edge"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "description": "Absolute URLs on the site's own hostname. At most 500.",
                    "example": [
                      "https://example.co.uk/about/"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "urls"
                ]
              }
            }
          }
        }
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/warm": {
      "get": {
        "summary": "Which edge regions a site keeps warm.",
        "operationId": "whichEdgeRegionsASiteKeepsWarm",
        "description": "A CDN caches per point of presence, so a warm run only warms the PoP\nnearest whatever ran it. Measured on a client site warmed from\nFalkenstein: a HIT in about 55ms from Frankfurt, and a MISS at 1.1-1.4s\nfrom London, where the readers actually were.\n\nNot the same axis as the site's storage region, which is one value and\nsays where the origin copies live.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "available": [
                      "UK",
                      "DE",
                      "US-East",
                      "US-West",
                      "Singapore",
                      "Thailand",
                      "Australia",
                      "Japan",
                      "India",
                      "Brazil",
                      "S-Africa"
                    ],
                    "allowance": 3,
                    "regions": [
                      "UK",
                      "DE"
                    ],
                    "pops": [
                      {
                        "hostname": "example.b-cdn.net",
                        "region": "UK",
                        "pop_code": "UK1-1486",
                        "edge_ip": "203.0.113.10",
                        "resolved_at": "2026-09-14T02:10:58.000000Z"
                      }
                    ],
                    "job": {
                      "site_id": 7,
                      "status": "active",
                      "interval_seconds": 604800,
                      "last_run_at": "2026-09-14T02:11:09.000000Z",
                      "last_warmed": 812,
                      "last_errors": 0
                    }
                  },
                  "properties": {
                    "available": {
                      "type": "array",
                      "example": [
                        "UK",
                        "DE",
                        "US-East",
                        "US-West",
                        "Singapore",
                        "Thailand",
                        "Australia",
                        "Japan",
                        "India",
                        "Brazil",
                        "S-Africa"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "allowance": {
                      "type": "integer",
                      "example": 3
                    },
                    "regions": {
                      "type": "array",
                      "example": [
                        "UK",
                        "DE"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "pops": {
                      "type": "array",
                      "example": [
                        {
                          "hostname": "example.b-cdn.net",
                          "region": "UK",
                          "pop_code": "UK1-1486",
                          "edge_ip": "203.0.113.10",
                          "resolved_at": "2026-09-14T02:10:58.000000Z"
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostname": {
                            "type": "string",
                            "example": "example.b-cdn.net"
                          },
                          "region": {
                            "type": "string",
                            "example": "UK"
                          },
                          "pop_code": {
                            "type": "string",
                            "example": "UK1-1486"
                          },
                          "edge_ip": {
                            "type": "string",
                            "example": "203.0.113.10"
                          },
                          "resolved_at": {
                            "type": "string",
                            "example": "2026-09-14T02:10:58.000000Z"
                          }
                        }
                      }
                    },
                    "job": {
                      "type": "object",
                      "properties": {
                        "site_id": {
                          "type": "integer",
                          "example": 7
                        },
                        "status": {
                          "type": "string",
                          "example": "active"
                        },
                        "interval_seconds": {
                          "type": "integer",
                          "example": 604800
                        },
                        "last_run_at": {
                          "type": "string",
                          "example": "2026-09-14T02:11:09.000000Z"
                        },
                        "last_warmed": {
                          "type": "integer",
                          "example": 812
                        },
                        "last_errors": {
                          "type": "integer",
                          "example": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Edge"
        ]
      },
      "put": {
        "summary": "Choose where a site stays warm.",
        "operationId": "chooseWhereASiteStaysWarm",
        "description": "Billed per location. Asking for more than the account has paid for is\nrefused with 402 rather than silently trimmed - a caller that asked for\nsix and got two would report six, and the only symptom would be\nAustralians finding the site slow.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "regions": [
                      "UK",
                      "DE"
                    ],
                    "ignored": []
                  },
                  "properties": {
                    "regions": {
                      "type": "array",
                      "example": [
                        "UK",
                        "DE"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "ignored": {
                      "type": "array",
                      "example": []
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "more regions than were paid for",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "over_allowance",
                    "asked": 6,
                    "allowance": 3,
                    "detail": "This account has paid to keep 3 locations warm. Buy more locations, or choose fewer."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "over_allowance"
                    },
                    "asked": {
                      "type": "integer",
                      "example": 6
                    },
                    "allowance": {
                      "type": "integer",
                      "example": 3
                    },
                    "detail": {
                      "type": "string",
                      "example": "This account has paid to keep 3 locations warm. Buy more locations, or choose fewer."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Edge"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "regions": {
                    "type": "array",
                    "description": "Catalogue keys from GET /v1/meta. An empty list pauses warming.",
                    "example": [
                      "UK",
                      "DE"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "interval_seconds": {
                    "type": "integer",
                    "description": "Defaults to 604800 (weekly). Nightly costs roughly 7x for no correctness gain - per-publish purges are what keep content fresh.",
                    "example": 604800
                  }
                },
                "required": [
                  "regions"
                ]
              }
            }
          }
        }
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/sites/{site}/warm/run": {
      "post": {
        "summary": "Ask for a full warm.",
        "operationId": "askForAFullWarm",
        "description": "Expressed as \"this job is overdue\" rather than running anything inline: a\nfull warm is thousands of requests and must not happen inside a request.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "queued": true,
                    "detail": "Marked overdue. The warm worker picks it up on its next tick; read GET /v1/sites/7/warm for the result."
                  },
                  "properties": {
                    "queued": {
                      "type": "boolean",
                      "example": true
                    },
                    "detail": {
                      "type": "string",
                      "example": "Marked overdue. The warm worker picks it up on its next tick; read GET /v1/sites/7/warm for the result."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Edge"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "site",
          "description": "The site: its numeric id, its label, or its\n  domain with or without scheme and \"www.\".",
          "example": "example.co.uk",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/api/v1/provisions": {
      "post": {
        "summary": "Build a domain out from nothing.",
        "operationId": "buildADomainOutFromNothing",
        "description": "Storage zone, pull zone, hostname, certificate, edge profile and key.\n\nQueued rather than run inline, and that is a security boundary rather\nthan a performance one: provisioning needs the CDN account key, which can\ndelete every zone we run for every client, so it never enters a process\nthat serves web requests. A worker running as the user that owns the key\npicks the row up instead, and the queue doubles as the audit trail.\n\nIt will not reach \"done\" until the hostname resolves, because a\ncertificate cannot be issued before that.",
        "parameters": [],
        "responses": {
          "202": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 41,
                    "status": "pending",
                    "detail": "Queued. The provisioner runs on a tick; poll GET /v1/provisions/41 for status and log. It will not be \"done\" until the certificate is issued, which needs the hostname to resolve first."
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 41
                    },
                    "status": {
                      "type": "string",
                      "example": "pending"
                    },
                    "detail": {
                      "type": "string",
                      "example": "Queued. The provisioner runs on a tick; poll GET /v1/provisions/41 for status and log. It will not be \"done\" until the certificate is issued, which needs the hostname to resolve first."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "one is already in flight for that domain",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "already_queued",
                    "detail": "A provision for example.co.uk is already pending."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "already_queued"
                    },
                    "detail": {
                      "type": "string",
                      "example": "A provision for example.co.uk is already pending."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Provisioning"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The domain to build out.",
                    "example": "example.co.uk"
                  },
                  "profile": {
                    "type": "string",
                    "description": "wordpress, woocommerce or static. woocommerce bypasses cache for carts and logins.",
                    "example": "wordpress"
                  },
                  "region": {
                    "type": "string",
                    "description": "Storage region - where origin copies live, not where the site is kept warm. Defaults to UK.",
                    "example": "UK"
                  }
                },
                "required": [
                  "domain",
                  "profile"
                ]
              }
            }
          }
        }
      },
      "get": {
        "summary": "Recent provisioning requests.",
        "operationId": "recentProvisioningRequests",
        "description": "",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "description": "1-100. Defaults to 25.",
            "example": 25,
            "required": false,
            "schema": {
              "type": "integer",
              "description": "1-100. Defaults to 25.",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "example": 41
                      },
                      "account_id": {
                        "type": "integer",
                        "example": 42
                      },
                      "domain": {
                        "type": "string",
                        "example": "example.co.uk"
                      },
                      "profile": {
                        "type": "string",
                        "example": "wordpress"
                      },
                      "region": {
                        "type": "string",
                        "example": "UK"
                      },
                      "status": {
                        "type": "string",
                        "example": "done"
                      },
                      "site_id": {
                        "type": "integer",
                        "example": 7
                      },
                      "log": {
                        "type": "string",
                        "example": "storage zone ok / pull zone ok / hostname ok / certificate ok"
                      },
                      "created_at": {
                        "type": "string",
                        "example": "2026-09-15 08:20:00"
                      },
                      "started_at": {
                        "type": "string",
                        "example": "2026-09-15 08:20:31"
                      },
                      "finished_at": {
                        "type": "string",
                        "example": "2026-09-15 08:21:12"
                      }
                    }
                  },
                  "example": [
                    {
                      "id": 41,
                      "account_id": 42,
                      "domain": "example.co.uk",
                      "profile": "wordpress",
                      "region": "UK",
                      "status": "done",
                      "site_id": 7,
                      "log": "storage zone ok / pull zone ok / hostname ok / certificate ok",
                      "created_at": "2026-09-15 08:20:00",
                      "started_at": "2026-09-15 08:20:31",
                      "finished_at": "2026-09-15 08:21:12"
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Provisioning"
        ]
      }
    },
    "/api/v1/provisions/{id}": {
      "get": {
        "summary": "Status and log for one provisioning request.",
        "operationId": "statusAndLogForOneProvisioningRequest",
        "description": "",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 41,
                    "account_id": 42,
                    "domain": "example.co.uk",
                    "profile": "wordpress",
                    "region": "UK",
                    "status": "done",
                    "site_id": 7,
                    "log": "storage zone ok / pull zone ok / hostname ok / certificate ok",
                    "created_at": "2026-09-15 08:20:00",
                    "started_at": "2026-09-15 08:20:31",
                    "finished_at": "2026-09-15 08:21:12"
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 41
                    },
                    "account_id": {
                      "type": "integer",
                      "example": 42
                    },
                    "domain": {
                      "type": "string",
                      "example": "example.co.uk"
                    },
                    "profile": {
                      "type": "string",
                      "example": "wordpress"
                    },
                    "region": {
                      "type": "string",
                      "example": "UK"
                    },
                    "status": {
                      "type": "string",
                      "example": "done"
                    },
                    "site_id": {
                      "type": "integer",
                      "example": 7
                    },
                    "log": {
                      "type": "string",
                      "example": "storage zone ok / pull zone ok / hostname ok / certificate ok"
                    },
                    "created_at": {
                      "type": "string",
                      "example": "2026-09-15 08:20:00"
                    },
                    "started_at": {
                      "type": "string",
                      "example": "2026-09-15 08:20:31"
                    },
                    "finished_at": {
                      "type": "string",
                      "example": "2026-09-15 08:21:12"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not on this account",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "not_found"
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "not_found"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Provisioning"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "description": "The request id.",
          "example": 41,
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ]
    },
    "/api/v1/analyse": {
      "post": {
        "summary": "Measure any public page.",
        "operationId": "measureAnyPublicPage",
        "description": "Fetches the page, then re-encodes up to forty of its images - real\nencoding of the real images, not a multiplier. The constant it replaced\ntold a site already running Peregrine that it could save 92%.\n\nWorks on any site, customer or not, so it is the call for qualifying\nprospects and for before-and-after evidence. Slow by nature and throttled\nto 20 a minute per token; a result for the same URL is reused for half an\nhour, which protects the site being analysed as much as it protects us.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "ok": true,
                    "url": "https://example.co.uk/",
                    "images_found": 18,
                    "images_measured": 18,
                    "bytes": {
                      "before": 2660001,
                      "after": 750884,
                      "saved": 1909117
                    },
                    "percent": 72,
                    "heaviest": [
                      {
                        "name": "hero.jpg",
                        "url": "https://example.co.uk/wp-content/uploads/hero.jpg",
                        "before": 486210,
                        "after": 61204
                      }
                    ],
                    "previews": []
                  },
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "url": {
                      "type": "string",
                      "example": "https://example.co.uk/"
                    },
                    "images_found": {
                      "type": "integer",
                      "example": 18
                    },
                    "images_measured": {
                      "type": "integer",
                      "example": 18
                    },
                    "bytes": {
                      "type": "object",
                      "properties": {
                        "before": {
                          "type": "integer",
                          "example": 2660001
                        },
                        "after": {
                          "type": "integer",
                          "example": 750884
                        },
                        "saved": {
                          "type": "integer",
                          "example": 1909117
                        }
                      }
                    },
                    "percent": {
                      "type": "integer",
                      "example": 72
                    },
                    "heaviest": {
                      "type": "array",
                      "example": [
                        {
                          "name": "hero.jpg",
                          "url": "https://example.co.uk/wp-content/uploads/hero.jpg",
                          "before": 486210,
                          "after": 61204
                        }
                      ],
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "example": "hero.jpg"
                          },
                          "url": {
                            "type": "string",
                            "example": "https://example.co.uk/wp-content/uploads/hero.jpg"
                          },
                          "before": {
                            "type": "integer",
                            "example": 486210
                          },
                          "after": {
                            "type": "integer",
                            "example": 61204
                          }
                        }
                      }
                    },
                    "previews": {
                      "type": "array",
                      "example": []
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "the page could not be fetched",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "Could not fetch that page (HTTP 403).",
                    "remedies": [
                      "Allow PeregrineBot/1.0 in your firewall"
                    ],
                    "blocked": true,
                    "http": 403
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "Could not fetch that page (HTTP 403)."
                    },
                    "remedies": {
                      "type": "array",
                      "example": [
                        "Allow PeregrineBot/1.0 in your firewall"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "blocked": {
                      "type": "boolean",
                      "example": true
                    },
                    "http": {
                      "type": "integer",
                      "example": 403
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Analysis"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The page to measure. Scheme optional.",
                    "example": "https://example.co.uk/"
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/monitors": {
      "get": {
        "summary": "Uptime monitors and their last result.",
        "operationId": "uptimeMonitorsAndTheirLastResult",
        "description": "A monitor answers \"does the site respond\". It would be perfectly happy\nwhile every image on it quietly reverted to a 4 MB original - that is\nwhat the delivery check is for.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "example": 12
                      },
                      "name": {
                        "type": "string",
                        "example": "Storefront"
                      },
                      "url": {
                        "type": "string",
                        "example": "https://example.co.uk/"
                      },
                      "method": {
                        "type": "string",
                        "example": "GET"
                      },
                      "expect_status": {
                        "type": "integer",
                        "example": 200
                      },
                      "interval_seconds": {
                        "type": "integer",
                        "example": 300
                      },
                      "status": {
                        "type": "string",
                        "example": "active"
                      },
                      "last_checked_at": {
                        "type": "string",
                        "example": "2026-09-15 08:19:02"
                      },
                      "last_ok": {
                        "type": "integer",
                        "example": 1
                      },
                      "last_ms": {
                        "type": "integer",
                        "example": 184
                      },
                      "last_status_code": {
                        "type": "integer",
                        "example": 200
                      },
                      "last_error": {
                        "type": "string",
                        "example": null,
                        "nullable": true
                      },
                      "fail_streak": {
                        "type": "integer",
                        "example": 0
                      },
                      "cert_expires_at": {
                        "type": "string",
                        "example": "2026-11-02 11:00:00"
                      }
                    }
                  },
                  "example": [
                    {
                      "id": 12,
                      "name": "Storefront",
                      "url": "https://example.co.uk/",
                      "method": "GET",
                      "expect_status": 200,
                      "interval_seconds": 300,
                      "status": "active",
                      "last_checked_at": "2026-09-15 08:19:02",
                      "last_ok": 1,
                      "last_ms": 184,
                      "last_status_code": 200,
                      "last_error": null,
                      "fail_streak": 0,
                      "cert_expires_at": "2026-11-02 11:00:00"
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Uptime"
        ]
      },
      "post": {
        "summary": "Add a monitor.",
        "operationId": "addAMonitor",
        "description": "The URL must resolve only to public addresses. A monitor is fetched on a\ntimer from two servers and its status code, latency and keyword result\nare readable here, so an unguarded one would be a scheduled scan of our\nown network with the results posted back.\n\n`alert_email` must be the account's own address or someone at the domain\nbeing monitored: an alert is mail from our sending domain carrying a\nsubject you choose, fired when a URL you choose fails.\n\nAt most 100 monitors per account.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 12,
                    "account_id": 42,
                    "name": "Storefront",
                    "url": "https://example.co.uk/",
                    "method": "GET",
                    "expect_status": 200,
                    "keyword": null,
                    "keyword_absent": null,
                    "interval_seconds": 300,
                    "timeout_seconds": 15,
                    "confirm_failures": 2,
                    "alert_email": "ops@example.co.uk",
                    "ssl_warn_days": 14,
                    "status": "active",
                    "created_at": "2026-09-15 08:22:00"
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 12
                    },
                    "account_id": {
                      "type": "integer",
                      "example": 42
                    },
                    "name": {
                      "type": "string",
                      "example": "Storefront"
                    },
                    "url": {
                      "type": "string",
                      "example": "https://example.co.uk/"
                    },
                    "method": {
                      "type": "string",
                      "example": "GET"
                    },
                    "expect_status": {
                      "type": "integer",
                      "example": 200
                    },
                    "keyword": {
                      "type": "string",
                      "example": null,
                      "nullable": true
                    },
                    "keyword_absent": {
                      "type": "string",
                      "example": null,
                      "nullable": true
                    },
                    "interval_seconds": {
                      "type": "integer",
                      "example": 300
                    },
                    "timeout_seconds": {
                      "type": "integer",
                      "example": 15
                    },
                    "confirm_failures": {
                      "type": "integer",
                      "example": 2
                    },
                    "alert_email": {
                      "type": "string",
                      "example": "ops@example.co.uk"
                    },
                    "ssl_warn_days": {
                      "type": "integer",
                      "example": 14
                    },
                    "status": {
                      "type": "string",
                      "example": "active"
                    },
                    "created_at": {
                      "type": "string",
                      "example": "2026-09-15 08:22:00"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "description": "the URL points inside a private network",
                      "type": "object",
                      "example": {
                        "message": "The url is not a public address we will fetch: host resolves to a non-public address.",
                        "errors": {
                          "url": [
                            "The url is not a public address we will fetch: host resolves to a non-public address. We only check hostnames that resolve to addresses on the public internet."
                          ]
                        }
                      },
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "The url is not a public address we will fetch: host resolves to a non-public address."
                        },
                        "errors": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "array",
                              "example": [
                                "The url is not a public address we will fetch: host resolves to a non-public address. We only check hostnames that resolve to addresses on the public internet."
                              ],
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    {
                      "description": "the alert address is neither yours nor the monitored domain's",
                      "type": "object",
                      "example": {
                        "error": "alert_email_not_allowed",
                        "detail": "Alerts can go to the account address (ops@acme.example) or to someone at the domain being monitored."
                      },
                      "properties": {
                        "error": {
                          "type": "string",
                          "example": "alert_email_not_allowed"
                        },
                        "detail": {
                          "type": "string",
                          "example": "Alerts can go to the account address (ops@acme.example) or to someone at the domain being monitored."
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Uptime"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Shown in alerts.",
                    "example": "Storefront"
                  },
                  "url": {
                    "type": "string",
                    "description": "Must resolve to a public address.",
                    "example": "https://example.co.uk/"
                  },
                  "method": {
                    "type": "string",
                    "description": "GET or HEAD. Defaults to GET.",
                    "example": "GET"
                  },
                  "expect_status": {
                    "type": "integer",
                    "description": "Defaults to 200.",
                    "example": 200
                  },
                  "keyword": {
                    "type": "string",
                    "description": "Must be present in the body.",
                    "example": "Add to basket"
                  },
                  "keyword_absent": {
                    "type": "string",
                    "description": "Must NOT be present in the body.",
                    "example": "Database error"
                  },
                  "interval_seconds": {
                    "type": "integer",
                    "description": "60-86400. Defaults to 300.",
                    "example": 300
                  },
                  "alert_email": {
                    "type": "string",
                    "description": "Where alerts go.",
                    "example": "ops@example.co.uk"
                  }
                },
                "required": [
                  "name",
                  "url"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/monitors/{id}": {
      "patch": {
        "summary": "Rename, pause or retime a monitor.",
        "operationId": "renamePauseOrRetimeAMonitor",
        "description": "",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 12,
                    "account_id": 42,
                    "name": "Storefront",
                    "url": "https://example.co.uk/",
                    "method": "GET",
                    "expect_status": 200,
                    "interval_seconds": 300,
                    "status": "paused",
                    "last_ok": 1,
                    "last_ms": 184
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 12
                    },
                    "account_id": {
                      "type": "integer",
                      "example": 42
                    },
                    "name": {
                      "type": "string",
                      "example": "Storefront"
                    },
                    "url": {
                      "type": "string",
                      "example": "https://example.co.uk/"
                    },
                    "method": {
                      "type": "string",
                      "example": "GET"
                    },
                    "expect_status": {
                      "type": "integer",
                      "example": 200
                    },
                    "interval_seconds": {
                      "type": "integer",
                      "example": 300
                    },
                    "status": {
                      "type": "string",
                      "example": "paused"
                    },
                    "last_ok": {
                      "type": "integer",
                      "example": 1
                    },
                    "last_ms": {
                      "type": "integer",
                      "example": 184
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Uptime"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "",
                    "example": "Storefront"
                  },
                  "status": {
                    "type": "string",
                    "description": "active or paused.",
                    "example": "paused"
                  },
                  "interval_seconds": {
                    "type": "integer",
                    "description": "60-86400.",
                    "example": 300
                  },
                  "alert_email": {
                    "type": "string",
                    "description": "",
                    "example": "ops@example.co.uk"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove a monitor.",
        "operationId": "removeAMonitor",
        "description": "",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "deleted": true
                  },
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not on this account",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "not_found"
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "not_found"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Uptime"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "description": "The monitor id.",
          "example": 12,
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ]
    },
    "/api/v1/monitors/{id}/checks": {
      "get": {
        "summary": "Recent checks for one monitor.",
        "operationId": "recentChecksForOneMonitor",
        "description": "The raw evidence behind an incident.",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "description": "1-500. Defaults to 100.",
            "example": 100,
            "required": false,
            "schema": {
              "type": "integer",
              "description": "1-500. Defaults to 100.",
              "example": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "example": 9044
                      },
                      "monitor_id": {
                        "type": "integer",
                        "example": 12
                      },
                      "checked_at": {
                        "type": "string",
                        "example": "2026-09-15 08:19:02"
                      },
                      "ok": {
                        "type": "integer",
                        "example": 1
                      },
                      "status_code": {
                        "type": "integer",
                        "example": 200
                      },
                      "ms": {
                        "type": "integer",
                        "example": 184
                      },
                      "note": {
                        "type": "string",
                        "example": null,
                        "nullable": true
                      },
                      "vantage": {
                        "type": "string",
                        "example": "primary"
                      }
                    }
                  },
                  "example": [
                    {
                      "id": 9044,
                      "monitor_id": 12,
                      "checked_at": "2026-09-15 08:19:02",
                      "ok": 1,
                      "status_code": 200,
                      "ms": 184,
                      "note": null,
                      "vantage": "primary"
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Uptime"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "description": "The monitor id.",
          "example": 12,
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ]
    },
    "/api/v1/audit": {
      "get": {
        "summary": "What has been changed through the API, and by which token.",
        "operationId": "whatHasBeenChangedThroughTheAPIAndByWhichToken",
        "description": "Write operations only; reads are not recorded, because an audit log that\nincludes them is one nobody reads. Exposed to the account rather than\nkept internal: the point of handing a token to an automation is that\nsomebody can afterwards read what it did, and a trail only the vendor can\nsee does not answer that.",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "description": "1-500. Defaults to 100.",
            "example": 100,
            "required": false,
            "schema": {
              "type": "integer",
              "description": "1-500. Defaults to 100.",
              "example": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "example": 318
                      },
                      "token_name": {
                        "type": "string",
                        "example": "Acme automation"
                      },
                      "action": {
                        "type": "string",
                        "example": "site.invalidate"
                      },
                      "context": {
                        "type": "string",
                        "example": "{\"site_id\":7,\"queued\":12}"
                      },
                      "ip": {
                        "type": "string",
                        "example": "203.0.113.44"
                      },
                      "created_at": {
                        "type": "string",
                        "example": "2026-09-15 08:18:40"
                      }
                    }
                  },
                  "example": [
                    {
                      "id": 318,
                      "token_name": "Acme automation",
                      "action": "site.invalidate",
                      "context": "{\"site_id\":7,\"queued\":12}",
                      "ip": "203.0.113.44",
                      "created_at": "2026-09-15 08:18:40"
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Governance"
        ]
      }
    },
    "/api/v1/tokens": {
      "get": {
        "summary": "List your tokens. Console session only.",
        "operationId": "listYourTokensConsoleSessionOnly",
        "description": "Refused for a bearer token: listing the names and abilities of an\naccount's other credentials hands whoever stole one a map of what to\nsteal next.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "example": 7
                      },
                      "name": {
                        "type": "string",
                        "example": "Acme automation"
                      },
                      "abilities": {
                        "type": "array",
                        "example": [
                          "read",
                          "purge",
                          "analyse"
                        ],
                        "items": {
                          "type": "string"
                        }
                      },
                      "last_used_at": {
                        "type": "string",
                        "example": "2026-09-15T08:14:02.000000Z"
                      },
                      "expires_at": {
                        "type": "string",
                        "example": null,
                        "nullable": true
                      },
                      "created_at": {
                        "type": "string",
                        "example": "2026-09-01T10:00:00.000000Z"
                      }
                    }
                  },
                  "example": [
                    {
                      "id": 7,
                      "name": "Acme automation",
                      "abilities": [
                        "read",
                        "purge",
                        "analyse"
                      ],
                      "last_used_at": "2026-09-15T08:14:02.000000Z",
                      "expires_at": null,
                      "created_at": "2026-09-01T10:00:00.000000Z"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "attempted with a token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "session_required",
                    "detail": "Tokens are managed from a signed-in console session, never by another token. Sign in at https://app.peregrinecdn.com/tokens."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "session_required"
                    },
                    "detail": {
                      "type": "string",
                      "example": "Tokens are managed from a signed-in console session, never by another token. Sign in at https://app.peregrinecdn.com/tokens."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Governance"
        ]
      },
      "post": {
        "summary": "Issue a token. Console session only.",
        "operationId": "issueATokenConsoleSessionOnly",
        "description": "A token that can mint tokens can widen its own abilities, which makes\nevery narrower ability decoration - so this refuses bearer authentication\nand requires a signed-in session.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "id": 8,
                    "name": "Acme automation",
                    "abilities": [
                      "read",
                      "purge",
                      "analyse"
                    ],
                    "expires_at": null,
                    "token": "8|shown once and never stored",
                    "warning": "Shown once. Store it where the client that needs it will read it, and nowhere else."
                  },
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 8
                    },
                    "name": {
                      "type": "string",
                      "example": "Acme automation"
                    },
                    "abilities": {
                      "type": "array",
                      "example": [
                        "read",
                        "purge",
                        "analyse"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "example": null,
                      "nullable": true
                    },
                    "token": {
                      "type": "string",
                      "example": "8|shown once and never stored"
                    },
                    "warning": {
                      "type": "string",
                      "example": "Shown once. Store it where the client that needs it will read it, and nowhere else."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "attempted with a token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "session_required",
                    "detail": "Tokens are managed from a signed-in console session, never by another token."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "session_required"
                    },
                    "detail": {
                      "type": "string",
                      "example": "Tokens are managed from a signed-in console session, never by another token."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Governance"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name it after the thing that will hold it - this is what appears in the audit trail.",
                    "example": "Barrie's Claude"
                  },
                  "abilities": {
                    "type": "array",
                    "description": "Any of read, purge, analyse, sites, warm, provision. Defaults to read, purge and analyse.",
                    "example": [
                      "read",
                      "purge",
                      "analyse"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "expires_in_days": {
                    "type": "integer",
                    "description": "1-365. Omit for a token that does not expire.",
                    "example": 90
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/tokens/{id}": {
      "delete": {
        "summary": "Revoke a token. Console session only.",
        "operationId": "revokeATokenConsoleSessionOnly",
        "description": "Refused for a bearer token: otherwise a read-only credential could turn\noff every other credential the account has, including the one an operator\nwould investigate with.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "revoked": true
                  },
                  "properties": {
                    "revoked": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Governance"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "description": "The token id.",
          "example": 7,
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ]
    },
    "/api/v1/site/status": {
      "get": {
        "summary": "Plugin connection check.",
        "operationId": "pluginConnectionCheck",
        "description": "Authenticates as a SITE, with the key held by the WordPress plugin - not\nan account token. That key lives in wp_options on a machine we do not\ncontrol, so it is scoped to one site's blast radius: a stolen key purges\nand warms that site and nothing else, cannot read another site, cannot\ntouch DNS or storage, and never reveals a CDN credential.\n\nReturns everything the plugin's settings screen shows, including the\nregion catalogue, so a new region becomes available to every site without\nshipping a plugin update.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "connected": true,
                    "site": {
                      "label": "example-co-uk",
                      "domain": "example.co.uk"
                    },
                    "media": {
                      "host": "https://images.example.co.uk",
                      "storage_zone": "pg-example-co-uk"
                    },
                    "warm": {
                      "available": [
                        "UK",
                        "DE",
                        "US-East"
                      ],
                      "regions": [
                        "UK"
                      ],
                      "pops": [
                        {
                          "region": "UK",
                          "pop_code": "UK1-1486",
                          "edge_ip": "203.0.113.10",
                          "resolved_at": "2026-09-14T02:10:58.000000Z"
                        }
                      ],
                      "status": "active",
                      "last_run_at": "2026-09-14T02:11:09.000000Z",
                      "last_warmed": 812,
                      "last_errors": 0,
                      "last_note": null,
                      "queue_pending": 0
                    }
                  },
                  "properties": {
                    "connected": {
                      "type": "boolean",
                      "example": true
                    },
                    "site": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "example": "example-co-uk"
                        },
                        "domain": {
                          "type": "string",
                          "example": "example.co.uk"
                        }
                      }
                    },
                    "media": {
                      "type": "object",
                      "properties": {
                        "host": {
                          "type": "string",
                          "example": "https://images.example.co.uk"
                        },
                        "storage_zone": {
                          "type": "string",
                          "example": "pg-example-co-uk"
                        }
                      }
                    },
                    "warm": {
                      "type": "object",
                      "properties": {
                        "available": {
                          "type": "array",
                          "example": [
                            "UK",
                            "DE",
                            "US-East"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "regions": {
                          "type": "array",
                          "example": [
                            "UK"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "pops": {
                          "type": "array",
                          "example": [
                            {
                              "region": "UK",
                              "pop_code": "UK1-1486",
                              "edge_ip": "203.0.113.10",
                              "resolved_at": "2026-09-14T02:10:58.000000Z"
                            }
                          ],
                          "items": {
                            "type": "object",
                            "properties": {
                              "region": {
                                "type": "string",
                                "example": "UK"
                              },
                              "pop_code": {
                                "type": "string",
                                "example": "UK1-1486"
                              },
                              "edge_ip": {
                                "type": "string",
                                "example": "203.0.113.10"
                              },
                              "resolved_at": {
                                "type": "string",
                                "example": "2026-09-14T02:10:58.000000Z"
                              }
                            }
                          }
                        },
                        "status": {
                          "type": "string",
                          "example": "active"
                        },
                        "last_run_at": {
                          "type": "string",
                          "example": "2026-09-14T02:11:09.000000Z"
                        },
                        "last_warmed": {
                          "type": "integer",
                          "example": 812
                        },
                        "last_errors": {
                          "type": "integer",
                          "example": 0
                        },
                        "last_note": {
                          "type": "string",
                          "example": null,
                          "nullable": true
                        },
                        "queue_pending": {
                          "type": "integer",
                          "example": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "the key is wrong, or the site is paused",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "invalid_key"
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "invalid_key"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Plugin (site key)"
        ]
      }
    },
    "/api/v1/site/invalidate": {
      "post": {
        "summary": "Plugin purge report.",
        "operationId": "pluginPurgeReport",
        "description": "The plugin says what changed and nothing more. URLs that are not on the\nsite's own hostname are skipped: without that, a stolen key could queue\npurges for any domain on the platform.\n\nTruncates at 500 rather than refusing, unlike the account route. A plugin\non somebody else's WordPress cannot be told to try again, so quietly\ncapping a runaway sitemap is the least-bad option here.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "queued": 3
                  },
                  "properties": {
                    "queued": {
                      "type": "integer",
                      "example": 3
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "the key is wrong, or the site is paused",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "invalid_key"
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "invalid_key"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Plugin (site key)"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "description": "Absolute URLs on this site's hostname.",
                    "example": [
                      "https://example.co.uk/about/"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "urls"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/site/warm": {
      "put": {
        "summary": "Always refused. Warm regions are an account decision.",
        "operationId": "alwaysRefusedWarmRegionsAreAnAccountDecision",
        "description": "Every extra region multiplies the warm: the same page set fetched again\nat another edge, and warming is billed egress. A site holding its own key\ncould otherwise select every region and multiply the bandwidth bill - and\nthe bill lands on us, not on whoever ticked the box.\n\nThe refusal lives here rather than merely being absent from the plugin's\nUI, because a hidden control is not an access control.",
        "parameters": [],
        "responses": {
          "403": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "not_permitted",
                    "detail": "Warm regions are set on the account, not from the site. Ask your provider to change them."
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "not_permitted"
                    },
                    "detail": {
                      "type": "string",
                      "example": "Warm regions are set on the account, not from the site. Ask your provider to change them."
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Plugin (site key)"
        ]
      }
    },
    "/api/v1/site/warm/run": {
      "post": {
        "summary": "Plugin warm request.",
        "operationId": "pluginWarmRequest",
        "description": "Marks the job overdue so the worker picks it up on its next tick.\nClearing running_since as well releases a claim left behind by a run that\ndied, which is otherwise only cleared after an hour.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "queued": true
                  },
                  "properties": {
                    "queued": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "the key is wrong, or the site is paused",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {
                    "error": "invalid_key"
                  },
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "invalid_key"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Plugin (site key)"
        ]
      }
    }
  }
}
