{"openapi":"3.1.0","info":{"title":"HereGo API — Video Evidence API for AI Agents","version":"1.0.0","summary":"The REST API for asking humans to produce visual evidence on demand.","description":"HereGo is the REST API for asking humans to produce visual evidence on demand.\n\nAI agents and applications can create video recording requests, generate browser-based recording links with a REST call, and retrieve the resulting videos programmatically.\n\n**Flow**\n\n1. An AI agent or application creates a visual-evidence request with `POST /v1/requests` and receives a `submission_url` plus a request-scoped `access_token`.\n2. The `submission_url` (`https://herego.video/r/{id}`) is sent to a human.\n3. The human opens the link in a browser and records a video without installing an app or creating a HereGo account.\n4. The agent checks request status with `GET /v1/requests/{id}`.\n5. Once `status` is `submitted`, the agent retrieves the video from `GET /v1/videos/{id}`.\n6. If the request is no longer needed, the agent can delete it with `DELETE /v1/requests/{id}`.\n\n**Public API surface**\n\n- `POST /v1/requests`\n- `GET /v1/requests/{id}`\n- `DELETE /v1/requests/{id}`\n- `GET /v1/videos/{id}`\n\n**Authentication and rate limits**\n\nNo API key is currently required for free anonymous usage.\n\nAnonymous usage is heavily rate limited.\n\nThe request-scoped `access_token` returned by `POST /v1/requests` authorizes access to that specific request and its submitted video, including request deletion.","contact":{"name":"HereGo Support","email":"support@herego.video"}},"externalDocs":{"description":"HereGo API documentation","url":"https://herego.video/docs/api"},"servers":[{"url":"https://herego.video/v1","description":"Production"}],"tags":[{"name":"Requests","description":"Create, inspect, and delete visual-evidence requests. A request is the primary resource; a submitted video is the human response."},{"name":"Videos","description":"Retrieve submitted videos through the HereGo API. Videos are never exposed as direct storage URLs."}],"security":[{"requestAccessToken":[]}],"paths":{"/v1/requests":{"post":{"operationId":"createRequest","tags":["Requests"],"summary":"Create a visual-evidence request","description":"Creates a new video request in the `awaiting_submission` state and returns a browser-based `submission_url` to send to a human, along with a request-scoped `access_token`.\n\nThe `access_token` is returned only once, at creation time, to the creator of the request. Store it securely: it is required to check status, delete the request, and stream the submitted video.\n\nNo API key is currently required. Anonymous creation is intentionally supported for the MVP and is aggressively rate limited (`429 Too Many Requests`).","security":[{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRequestBody"},"example":{"prompt":"Please show me what happens when you try to check out."}}}},"responses":{"201":{"description":"Request created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedRequest"},"example":{"id":"req_abc123","status":"awaiting_submission","submission_url":"https://herego.video/r/abc123","access_token":"rat_9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d","expires_at":"2026-09-08T14:30:00Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"operationId":"deleteRequest","tags":["Requests"],"summary":"Delete a visual-evidence request","description":"Deletes a video request and cleans up any associated submitted video. Access is scoped to the specific request via the request access token.\n\nThis operation is useful when an AI agent or application no longer needs the request or its submitted evidence. From the caller's perspective it is idempotent: once deleted, subsequent reads return `404 Not Found`.","responses":{"204":{"description":"Request deleted. No content."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/requests/{id}":{"parameters":[{"$ref":"#/components/parameters/RequestId"}],"get":{"operationId":"getRequest","tags":["Requests"],"summary":"Get the status of a visual-evidence request","description":"Returns the current status of a video request. Access is scoped to the specific request: the caller must present the request access token returned when the request was created.\n\nWhen `status` is `submitted`, the response includes a `video` object whose `url` is a HereGo API endpoint (`GET /v1/videos/{id}`), **not** a direct storage URL. Stream the video from that URL using the same request access token.\n\n**Status values**\n\n- `awaiting_submission` — the request exists but no video has been submitted yet.\n- `submitted` — a human has submitted a video; `video` is present.\n- `expired` — the request (and any video) has passed its `expires_at`; the video is no longer retrievable.\n\n**Lifecycle transitions**\n\n- `awaiting_submission → submitted`\n- `awaiting_submission → expired`\n- `submitted → expired`","responses":{"200":{"description":"Current request status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestStatus"},"examples":{"awaitingSubmission":{"summary":"Before submission","value":{"id":"req_abc123","status":"awaiting_submission"}},"submitted":{"summary":"After submission","value":{"id":"req_abc123","status":"submitted","video":{"id":"vid_xyz789","url":"https://herego.video/v1/videos/vid_xyz789","expires_at":"2026-09-08T14:30:00Z"}}},"expired":{"summary":"After expiry","value":{"id":"req_abc123","status":"expired"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/videos/{id}":{"parameters":[{"$ref":"#/components/parameters/VideoId"}],"get":{"operationId":"streamVideo","tags":["Videos"],"summary":"Stream a submitted video","description":"Streams the submitted video for a request. The response body is a binary video stream served through HereGo; the underlying private storage object is never exposed.\n\n**Authorization**\n\nRequires the request access token for the request that owns this video. HereGo validates the request-scoped token and rejects mismatched or missing tokens.\n\n**Expiry**\n\nHereGo checks the video's `expires_at` on every request. After expiry, the video is no longer served and the endpoint returns `410 Gone`. Note: HereGo cannot prevent a client from retaining bytes it already downloaded before expiry; expiry only prevents new access.\n\n**HTTP Range / streaming**\n\nThe endpoint supports HTTP partial-content semantics. Clients and agents may send a `Range: bytes=start-end` header for partial retrieval, seeking, or streaming. A valid range request returns `206 Partial Content` with `Content-Range` and `Accept-Ranges: bytes`. A request without a `Range` header may return the complete `200 OK` response with the appropriate `Content-Type` and `Content-Length`.\n\nThis is the sole video retrieval/streaming endpoint. There is no public `/v/{id}` viewer route and no permanent media URL.","parameters":[{"name":"Range","in":"header","required":false,"description":"Optional HTTP byte range for partial retrieval, e.g. `bytes=0-1048575`. When present and satisfiable, the response is `206 Partial Content`.","schema":{"type":"string","examples":["bytes=0-1048575"]}}],"responses":{"200":{"description":"Full video stream. Returned when no `Range` header is provided (or the server chooses to return the complete representation).","headers":{"Accept-Ranges":{"$ref":"#/components/headers/AcceptRanges"},"Content-Type":{"$ref":"#/components/headers/VideoContentType"},"Content-Length":{"$ref":"#/components/headers/ContentLength"}},"content":{"video/*":{"schema":{"$ref":"#/components/schemas/VideoBinary"}}}},"206":{"description":"Partial video stream. Returned in response to a satisfiable `Range` request.","headers":{"Accept-Ranges":{"$ref":"#/components/headers/AcceptRanges"},"Content-Range":{"$ref":"#/components/headers/ContentRange"},"Content-Type":{"$ref":"#/components/headers/VideoContentType"},"Content-Length":{"$ref":"#/components/headers/ContentLength"}},"content":{"video/*":{"schema":{"$ref":"#/components/schemas/VideoBinary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"416":{"description":"The requested range is not satisfiable.","headers":{"Content-Range":{"$ref":"#/components/headers/ContentRange"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}}},"components":{"securitySchemes":{"requestAccessToken":{"type":"http","scheme":"bearer","bearerFormat":"Request access token","description":"Request-scoped bearer token returned by `POST /v1/requests`. Authorizes access to a single request and its submitted video. Distinct from an API key (which identifies an account and is not currently required). Send as `Authorization: Bearer <access_token>`."}},"parameters":{"RequestId":{"name":"id","in":"path","required":true,"description":"Identifier of the video request, e.g. `req_abc123`.","schema":{"type":"string","examples":["req_abc123"]}},"VideoId":{"name":"id","in":"path","required":true,"description":"Identifier of the submitted video, e.g. `vid_xyz789`.","schema":{"type":"string","examples":["vid_xyz789"]}}},"headers":{"AcceptRanges":{"description":"Indicates that the endpoint supports byte-range requests.","schema":{"type":"string","examples":["bytes"]}},"ContentRange":{"description":"The byte range returned and the total size, e.g. `bytes 0-1048575/5242880`.","schema":{"type":"string","examples":["bytes 0-1048575/5242880"]}},"VideoContentType":{"description":"The media type of the video stream.","schema":{"type":"string","examples":["video/webm","video/mp4"]}},"ContentLength":{"description":"The number of bytes in this response body.","schema":{"type":"integer","format":"int64","examples":[1048576]}}},"schemas":{"CreateRequestBody":{"type":"object","required":["prompt","expires_in_hours"],"additionalProperties":false,"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000,"description":"Human-readable instruction shown to the person who will record the video.","examples":["Please show me what happens when you try to check out."]},"expires_in_hours":{"type":"integer","minimum":1,"maximum":48,"description":"Number of hours until the request and any submitted video expire. Must be between 1 and 48 hours.","examples":[24]}}},"RequestStatusValue":{"type":"string","description":"Lifecycle state of a video request.","enum":["awaiting_submission","submitted","expired"]},"CreatedRequest":{"type":"object","required":["id","status","submission_url","access_token","expires_at"],"properties":{"id":{"type":"string","description":"Identifier of the created request.","examples":["req_abc123"]},"status":{"$ref":"#/components/schemas/RequestStatusValue","description":"Always `awaiting_submission` immediately after creation.","examples":["awaiting_submission"]},"submission_url":{"type":"string","format":"uri","description":"Public website URL (`/r/{id}`) to send to a human so they can record and submit the video.","examples":["https://herego.video/r/abc123"]},"access_token":{"type":"string","description":"Request-scoped bearer token. Returned only once, to the creator. Required to check status, delete the request, and stream the submitted video.","examples":["rat_9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d"]},"expires_at":{"type":"string","format":"date-time","description":"When the request (and any submitted video) expires.","examples":["2026-09-08T14:30:00Z"]}}},"SubmittedVideo":{"type":"object","required":["id","url","expires_at"],"properties":{"id":{"type":"string","description":"Identifier of the submitted video.","examples":["vid_xyz789"]},"url":{"type":"string","format":"uri","description":"HereGo API endpoint that streams the video (`GET /v1/videos/{id}`). This is NOT a direct storage URL. Stream it using the request access token.","examples":["https://herego.video/v1/videos/vid_xyz789"]},"expires_at":{"type":"string","format":"date-time","description":"When the video stops being served. After this time, streaming returns `410 Gone`.","examples":["2026-09-08T14:30:00Z"]}}},"RequestStatus":{"type":"object","required":["id","status","expires_at"],"properties":{"id":{"type":"string","examples":["req_abc123"]},"status":{"$ref":"#/components/schemas/RequestStatusValue"},"expires_at":{"type":"string","format":"date-time","description":"When the request and any submitted video expire.","examples":["2026-09-08T14:30:00Z"]},"video":{"$ref":"#/components/schemas/SubmittedVideo","description":"Present only when `status` is `submitted`."}}},"VideoBinary":{"type":"string","format":"binary","description":"Raw binary video stream. Supports full (`200 OK`) and partial (`206 Partial Content`) responses."},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code.","examples":["not_found","unauthorized","gone","rate_limited"]},"message":{"type":"string","description":"Human-readable explanation of the error.","examples":["The requested resource was not found."]}}}}}},"responses":{"BadRequest":{"description":"The request was malformed or failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"bad_request","message":"Field 'prompt' is required."}}}}},"Unauthorized":{"description":"Missing or invalid request access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unauthorized","message":"A valid request access token is required."}}}}},"Forbidden":{"description":"The provided access token does not grant access to this request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"forbidden","message":"This access token is not scoped to the requested resource."}}}}},"NotFound":{"description":"The request or video does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"The requested resource was not found."}}}}},"Gone":{"description":"The request or video has expired and is no longer available. HereGo stops serving the video after `expires_at`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"gone","message":"This video has expired and is no longer available."}}}}},"TooManyRequests":{"description":"Rate limit exceeded. Anonymous access is aggressively rate limited in the MVP.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","examples":[30]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Please retry later."}}}}},"InternalServerError":{"description":"An unexpected error occurred.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"An unexpected error occurred."}}}}}}}}