{
  "openapi": "3.0.3",
  "info": {
    "title": "Pulse upload protocol",
    "description": "The HTTP surface of the Pulse upload protocol, as implemented by @mieweb/pulsevault. Generated from the plugin's route schemas — see PROTOCOL.md for the full contract.",
    "version": "2.3"
  },
  "components": {
    "schemas": {}
  },
  "paths": {
    "/pulsevault/upload": {
      "post": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "head": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "options": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pulsevault/upload/{*}": {
      "post": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "*",
            "required": true
          }
        ],
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "*",
            "required": true
          }
        ],
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "head": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "*",
            "required": true
          }
        ],
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "*",
            "required": true
          }
        ],
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "options": {
        "summary": "TUS resumable upload endpoint",
        "tags": [
          "pulsevault"
        ],
        "description": "TUS v1 resumable upload protocol.\n\n- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n  - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n  - `filename` — original filename; the extension must match the kind's allowed list.\n  - `kind` — `video` (default), `project`, `captions`, or `thumbnail`. Determines the storage subdir and which completion hooks fire.\n  - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. the captions, beat manifest or thumbnail belonging to a pulse's video).\n  - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n- `HEAD` returns the current offset for a resumable upload.\n- `DELETE` removes the upload and its artifact, whether in flight (a cancel) or finished. Authorized as `delete`.\n\nSee https://tus.io/protocols/resumable-upload for the full protocol.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "*",
            "required": true
          }
        ],
        "responses": {
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Invalid request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pulsevault/capabilities": {
      "get": {
        "summary": "Discover this deployment's protocol version and configuration",
        "tags": [
          "pulsevault"
        ],
        "description": "Unauthenticated — the response carries no secrets. Lets a client detect protocol compatibility, allowed artifact kinds/extensions and the upload size cap before pairing.",
        "responses": {
          "200": {
            "description": "Unauthenticated discovery (PROTOCOL.md §2). A client reads it before pairing and pairs only if its protocol range overlaps [minSupportedVersion, maxSupportedVersion]. Clients must ignore fields they don't recognize.",
            "content": {
              "application/json": {
                "schema": {
                  "title": "GET /capabilities response",
                  "description": "Unauthenticated discovery (PROTOCOL.md §2). A client reads it before pairing and pairs only if its protocol range overlaps [minSupportedVersion, maxSupportedVersion]. Clients must ignore fields they don't recognize.",
                  "type": "object",
                  "required": [
                    "protocolVersion",
                    "protocolRevision",
                    "minSupportedVersion",
                    "maxSupportedVersion",
                    "kinds",
                    "allowedExtensions",
                    "maxUploadSize",
                    "checksum"
                  ],
                  "properties": {
                    "protocolVersion": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Protocol major this server implements. Also sent as the `Protocol-Version` header on every response."
                    },
                    "protocolRevision": {
                      "type": "string",
                      "pattern": "^[0-9]+\\.[0-9]+$",
                      "description": "Spec revision this server implements, `major.minor`. The minor counts additions older clients can ignore."
                    },
                    "minSupportedVersion": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Oldest protocol major this server accepts. Clients whose newest protocol is older get 426 Upgrade Required."
                    },
                    "maxSupportedVersion": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Newest protocol major this server accepts."
                    },
                    "kinds": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "video",
                          "project",
                          "captions",
                          "thumbnail"
                        ]
                      },
                      "description": "Artifact kinds this server accepts."
                    },
                    "allowedExtensions": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "description": "Allowed file extensions per kind, lowercase with the leading dot."
                    },
                    "maxUploadSize": {
                      "type": "number",
                      "description": "Largest artifact the server accepts, in bytes."
                    },
                    "checksum": {
                      "type": "object",
                      "required": [
                        "algorithms"
                      ],
                      "properties": {
                        "algorithms": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      },
                      "description": "Checksum algorithms accepted in `Upload-Metadata.checksum`."
                    },
                    "viewLinks": {
                      "type": "boolean",
                      "description": "Whether `POST {prefix}/artifacts/<id>/view-link` mints read-only view links (§6.4). Absent before protocol 2.2, which a client treats as false."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/pulsevault/artifacts/{artifactId}": {
      "delete": {
        "summary": "Delete an uploaded artifact",
        "tags": [
          "pulsevault"
        ],
        "description": "Deletes all storage for an artifactId (bytes + sidecar metadata), regardless of kind. Runs the `authorize` hook with `phase: \"delete\"` before the adapter's `remove` is called. Returns 204 on success, 404 if the artifactId was unknown, 501 if the adapter does not implement `remove`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "in": "path",
            "name": "artifactId",
            "required": true,
            "description": "UUID of the upload to delete."
          }
        ],
        "responses": {
          "400": {
            "description": "`artifactId` is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "`artifactId` is not a valid UUID.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Artifact not found.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Artifact not found.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "501": {
            "description": "Storage adapter does not implement delete.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Storage adapter does not implement delete.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "Serve a previously uploaded artifact",
        "tags": [
          "pulsevault"
        ],
        "description": "Resolves the `artifactId` through the configured storage adapter and either streams the bytes or redirects (for CDN-backed adapters). The artifact's kind (video, project, or captions) is resolved from storage, not the URL. Runs the `authorize` hook before resolve.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "token",
            "required": false,
            "description": "Optional bearer token for pre-authenticated watch links. Forwarded to the `authorize` hook as `ctx.token` so parent servers can validate it without a separate login step."
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "in": "path",
            "name": "artifactId",
            "required": true,
            "description": "UUID returned from the upload flow."
          }
        ],
        "responses": {
          "400": {
            "description": "`artifactId` is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "`artifactId` is not a valid UUID.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Artifact not found.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Artifact not found.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pulsevault/artifacts/{artifactId}/view-link": {
      "post": {
        "summary": "Mint a read-only view link for a finished artifact",
        "tags": [
          "pulsevault"
        ],
        "description": "Returns a token that opens the artifact (and the artifacts `relatedTo` it) as `GET /artifacts/:artifactId?token=`, and nothing more — no uploads, deletes or further links. Runs the `authorize` hook with `phase: \"share\"`; the host's `issueViewLink` decides how long the link works. 404 unless the server enables view links (`/capabilities` `viewLinks`) and the artifact is finished; 403 when the host refuses a link for it.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "in": "path",
            "name": "artifactId",
            "required": true,
            "description": "UUID of the finished artifact to link to."
          }
        ],
        "responses": {
          "200": {
            "description": "A read-only view link to a finished artifact (PROTOCOL.md §6.4, protocol 2.2). The token opens the artifact, and the artifacts `relatedTo` it, as `GET {prefix}/artifacts/<id>?token=<token>` — nothing more, so it is safe to share. The server decides how long it works.",
            "content": {
              "application/json": {
                "schema": {
                  "title": "POST /artifacts/<id>/view-link response",
                  "description": "A read-only view link to a finished artifact (PROTOCOL.md §6.4, protocol 2.2). The token opens the artifact, and the artifacts `relatedTo` it, as `GET {prefix}/artifacts/<id>?token=<token>` — nothing more, so it is safe to share. The server decides how long it works.",
                  "type": "object",
                  "required": [
                    "token",
                    "expiresAt"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "token": {
                      "type": "string",
                      "minLength": 1,
                      "description": "The view token. Opaque to clients."
                    },
                    "expiresAt": {
                      "type": "integer",
                      "description": "When the link stops working, in seconds since the Unix epoch."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`artifactId` is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "`artifactId` is not a valid UUID.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request, or the host refused a link for this artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request, or the host refused a link for this artifact.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "View links are not enabled, or the artifact is not found or not finished.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "View links are not enabled, or the artifact is not found or not finished.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pulsevault/artifacts/{artifactId}/status": {
      "get": {
        "summary": "Where an upload is: uploading, processing or ready",
        "tags": [
          "pulsevault"
        ],
        "description": "Reports the artifact's state (`unknown`, `uploading`, `processing` while a web-ready conversion rewrites it, `ready`), its kind and `relatedTo`, the bytes received against its declared length, whether the host's `onUploadComplete` has finished (`acknowledged`), and what the host recorded with `recordOutcome` (`outcome`). Runs the `authorize` hook with `phase: \"status\"`; `createCapabilityAuthorize` grants it to the pairing token and to a view token. Never cached.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "token",
            "required": false,
            "description": "Optional bearer token (the pairing token or a view token), forwarded to the `authorize` hook as `ctx.token` for browsers that can’t set a header."
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "in": "path",
            "name": "artifactId",
            "required": true,
            "description": "UUID of the upload to report on."
          }
        ],
        "responses": {
          "200": {
            "description": "Where an upload is (PROTOCOL.md §6.5, protocol 2.3): for a page waiting on it, so the host needs no table of its own. Authorized like opening the artifact. Never cached.",
            "content": {
              "application/json": {
                "schema": {
                  "title": "GET /artifacts/<id>/status response",
                  "description": "Where an upload is (PROTOCOL.md §6.5, protocol 2.3): for a page waiting on it, so the host needs no table of its own. Authorized like opening the artifact. Never cached.",
                  "type": "object",
                  "required": [
                    "artifactId",
                    "state"
                  ],
                  "properties": {
                    "artifactId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The artifact asked about."
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "unknown",
                        "uploading",
                        "processing",
                        "ready"
                      ],
                      "description": "`unknown` for an id the server has never seen; `uploading` until the final byte; `processing` while the server converts the bytes for the web; `ready` once the artifact is served."
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "video",
                        "project",
                        "captions",
                        "thumbnail"
                      ],
                      "description": "Artifact kind. Absent when `state` is `unknown`."
                    },
                    "relatedTo": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The video this artifact belongs to, if it declared one."
                    },
                    "name": {
                      "type": "string",
                      "description": "The display name the client sent (`Upload-Metadata.name`), if any."
                    },
                    "bytesReceived": {
                      "type": "number",
                      "description": "Bytes received so far while `uploading`, or stored once finished, when the server knows."
                    },
                    "size": {
                      "type": "number",
                      "description": "The upload's declared length, when the server knows."
                    },
                    "acknowledged": {
                      "type": "boolean",
                      "description": "Whether the host has finished handling the completed upload (its `onUploadComplete` returned)."
                    },
                    "outcome": {
                      "description": "What the host recorded about where the upload went, or why it didn't. Any JSON; absent until the host records one."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`artifactId` is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "`artifactId` is not a valid UUID.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pulsevault/artifacts/{artifactId}/poster": {
      "get": {
        "summary": "Serve a video's poster frame",
        "tags": [
          "pulsevault"
        ],
        "description": "Streams or redirects to the finished thumbnail `relatedTo` the video, exactly as `GET /artifacts/:artifactId` would serve it. Runs the `authorize` hook with `phase: \"resolve\"` on the video, so whoever may watch it may see its poster. 404 until the poster has landed.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "token",
            "required": false,
            "description": "Optional bearer token for pre-authenticated watch links, forwarded to the `authorize` hook as `ctx.token`."
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "in": "path",
            "name": "artifactId",
            "required": true,
            "description": "UUID of the video (the pulse's anchor artifact)."
          }
        ],
        "responses": {
          "400": {
            "description": "`artifactId` is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "`artifactId` is not a valid UUID.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authorize hook rejected the request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Authorize hook rejected the request.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The video has no finished poster frame.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "The video has no finished poster frame.",
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "pulsevault",
      "description": "Routes mounted by the plugin"
    }
  ]
}
