Three surfaces move raw bytes rather than JSON-RPC. They are plain HTTP because they stream binary data.

Upload — POST /api/v1/images

Multipart form upload of an original. Requires a bearer token (an access token or a personal access token).

  • file (required) — the image file (png, jpg, or webp).
  • meta (optional) — a JSON string, {"title":"…","description":"…"}.

On success returns 201 with the created image (the same shape as image.get). The original is stored privately and its renders are enqueued; the image starts in a non-ready state and becomes ready once its renders are generated.

curl -s https://peinture.gumeniuk.com/api/v1/images \
  -H "Authorization: Bearer $TOKEN" \
  -F 'file=@sunset.jpg' \
  -F 'meta={"title":"Sunset","description":"From the pier"}'

Errors: 400 (missing/empty/unsupported image), 401 (bad token), 413 (too large).

Archive — GET /api/v1/images/{id}/archive?token=…

Downloads a zip of every render plus the original. The token comes from image.archiveToken — a 5-minute, owner-scoped link. Ready images only. Returns application/zip.

Render serve — GET /i/{id}/{crop}.{ext}

Streams a single public render through the app (see serving modes). Never serves originals. Public — no token required.