These endpoints are live in this preview but not yet public — there's no API key issuance, and the shape may still change before a stable release.
Join the API waitlistOverview
This app exposes a small, same-origin job API: upload an image, submit a restore or upscale job, then poll for its result. There are no API keys yet — every request is scoped to your browser's guest session cookie.
- Base URL
- /apiAll paths below are relative to this same-origin base — there is no separate API host to configure.
- Authentication
- Session-scoped via an httpOnly cookie issued by continuing as a guest. No API keys exist yet (see "Planned" below).
Contract guarantee: a job's result_url is always a valid image_url — you can submit it directly to /api/restore or /api/upscale to chain another operation (this is exactly how "Upscale this image" works after a Restore).
/api/uploadsUpload an image
Uploads a single image file and returns a same-origin asset URL you can pass as image_url to Restore or Upscale.
Request body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| file | binary (multipart/form-data) | Yes | — | The image file, sent as multipart/form-data. |
Response body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| image_url | string | Yes | — | A same-origin asset URL returned by a previous upload — the only accepted image_url shape. |
| bytes | number | Yes | 0–9007199254740991 | The stored file size in bytes. |
| mime | string | Yes | — | The sniffed MIME type of the stored file (authoritative — not the client-declared type). |
201 response example
{
"image_url": "/api/mock-assets/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"bytes": 2458112,
"mime": "image/jpeg"
}Code samples
curl -X POST 'https://your-app.example.com/api/uploads' \
-F "file=@photo.jpg"/api/restoreCreate a restore job
Starts an asynchronous restore job for a previously uploaded image. Returns immediately with a pending job reference — poll GET /api/jobs/{job_id} for progress.
Request body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| image_url | string | Yes | /api/mock-assets/<uuid> | A same-origin asset URL returned by a previous upload — the only accepted image_url shape. |
| user_hint | string | No | max 300 chars | An optional free-text note describing the photo, used to steer the mock analysis step. |
| params.colorize | boolean | Yes | — | Whether a faded color photo should be recolored. Black-and-white photos are never colorized. |
Response body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| job_id | string | Yes | job_<uuid> | The unique identifier for this job. |
| status | pending | processing | succeeded | failed | cancelled | Yes | — | The job's current lifecycle state. |
202 response example
{
"job_id": "job_3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "pending"
}Code samples
curl -X POST 'https://your-app.example.com/api/restore' \
-H 'Content-Type: application/json' \
-d '{ "image_url": "/api/mock-assets/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "user_hint": "the sky was blue", "params": { "colorize": true }}'/api/upscaleCreate an upscale job
Starts an asynchronous upscale job for a previously uploaded image, at the requested mode and target resolution. Returns immediately with a pending job reference.
Request body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| image_url | string | Yes | /api/mock-assets/<uuid> | A same-origin asset URL returned by a previous upload — the only accepted image_url shape. |
| user_hint | string | No | max 300 chars | An optional free-text note describing the photo, used to steer the mock analysis step. |
| params.mode | fast | balanced | high | Yes | — | The upscale quality/speed tradeoff. |
| params.target_megapixels | number | Yes | 1–64 | The desired output resolution, in megapixels. |
Response body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| job_id | string | Yes | job_<uuid> | The unique identifier for this job. |
| status | pending | processing | succeeded | failed | cancelled | Yes | — | The job's current lifecycle state. |
202 response example
{
"job_id": "job_3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "pending"
}Code samples
curl -X POST 'https://your-app.example.com/api/upscale' \
-H 'Content-Type: application/json' \
-d '{ "image_url": "/api/mock-assets/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "params": { "mode": "fast", "target_megapixels": 12 }}'/api/jobs/{job_id}Get job status
Returns the current state of a job: status, progress, and — once succeeded — a result_url you can download or resubmit as a new image_url.
Response body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| job_id | string | Yes | job_<uuid> | The unique identifier for this job. |
| type | restore | upscale | Yes | — | Which tool created this job. |
| status | pending | processing | succeeded | failed | cancelled | Yes | — | The job's current lifecycle state. |
| progress | number | Yes | 0–100 | Percent complete, 0–100. |
| result_url | string | No | — | Present once the job succeeds — a same-origin asset URL that can also be submitted as a new image_url. |
| error.code | INVALID_REQUEST | UNSUPPORTED_FORMAT | FILE_TOO_LARGE | JOB_NOT_FOUND | VLM_FAILED | WORKFLOW_FAILED | TIMEOUT | CANCELLED | INTERNAL | Yes | — | The machine-readable failure reason. |
| error.message | string | Yes | — | A human-readable failure message. Present only when status is failed. |
| created_at | string | Yes | — | When the job was created, in ISO 8601. |
| updated_at | string | Yes | — | When the job was last updated, in ISO 8601. |
200 response example
{
"job_id": "job_3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "restore",
"status": "succeeded",
"progress": 100,
"result_url": "/api/mock-assets/6c1d3a2e-4f5b-4a1e-9c2d-1a2b3c4d5e6f",
"created_at": "2026-08-03T10:15:00.000Z",
"updated_at": "2026-08-03T10:15:42.000Z"
}Code samples
curl -X GET 'https://your-app.example.com/api/jobs/job_3fa85f64-5717-4562-b3fc-2c963f66afa6'/api/jobs/{job_id}/cancelCancel a job
Requests cancellation of a job. Cancelling an already-terminal job is a no-op that returns its current, unchanged state.
Response body
| Name | Type | Required | Bounds | Description |
|---|---|---|---|---|
| job_id | string | Yes | job_<uuid> | The unique identifier for this job. |
| status | pending | processing | succeeded | failed | cancelled | Yes | — | The job's current lifecycle state. |
202 response example
{
"job_id": "job_3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "cancelled"
}Code samples
curl -X POST 'https://your-app.example.com/api/jobs/job_3fa85f64-5717-4562-b3fc-2c963f66afa6/cancel'Job status lifecycle
Every job moves through this state machine. pending and processing are non-terminal; succeeded, failed and cancelled are terminal and never change again.
If a job doesn't reach a terminal state within the client's polling deadline, it is treated as a TIMEOUT failure.
Error codes
Every error uses this fixed set of codes. INVALID_REQUEST, UNSUPPORTED_FORMAT, FILE_TOO_LARGE and JOB_NOT_FOUND are returned as HTTP error responses; the rest surface as error.code inside a job object once that job reaches a terminal failed or cancelled state.
| Code | Where it appears | Meaning |
|---|---|---|
| INVALID_REQUEST | HTTP 400 error response | This request is invalid. Please check your input and try again. |
| UNSUPPORTED_FORMAT | HTTP 415 error response | This file format isn't supported. Please use JPEG, PNG, WebP, HEIC, or HEIF. |
| FILE_TOO_LARGE | HTTP 413 error response | This file is too large. Please use a smaller image. |
| JOB_NOT_FOUND | HTTP 404 error response | We couldn't find this job. It may have expired. |
| VLM_FAILED | Inside a terminal job's error field | We couldn't analyze this image. Please try again. |
| WORKFLOW_FAILED | Inside a terminal job's error field | Processing failed. Please try again. |
| TIMEOUT | Inside a terminal job's error field | This is taking longer than expected. Please try again. |
| CANCELLED | Inside a terminal job's error field | This job was cancelled. |
| INTERNAL | HTTP 500 error response | Something went wrong on our end. Please try again. |
A versioned /v1 gateway for third-party integrations is planned — API keys instead of session cookies, job-completion webhooks instead of polling, and per-key rate limits. None of this is implemented yet, and no request/response shape, authentication scheme, webhook signature or rate-limit number is committed. Treat everything above this section — the same-origin /api routes this app actually serves — as the only contract in force today. If and when /v1 ships, it will be documented on its own with a full specification; this paragraph is a heads-up, not a preview.