Wallpaper API

The public API has a wallpaper resource and a matching source-status resource. The wallpaper supports GET for an image body and HEAD for metadata only.

GET /api/wallpaper?categories=vocabulary,coding,science&theme=nature&size=standard

An OpenAPI description is available at /openapi.json.

Query parameters

NameDefaultAllowed values
categoriesvocabularyOne to eight comma-separated values from vocabulary, coding, finance, stoicism, science, history, psychology, productivity
themenatureDaily photography: nature, mountains, ocean, forest, space. Fixed WallCab Originals: amoled, minimal, abstract, gradient, monochrome, grid
sizestandardstandard, air, max
backgroundnoneOpaque 22-character ID returned by the custom upload flow
notenoneOptional personal text from 1 to 80 characters

standard is 1206×2622, air is 1260×2736, and max is 1320×2868. Older iPhones can use standard; iOS will scale or crop the image.

WallCab normalizes the selected interests and chooses one for each UTC day. The choice looks varied but remains stable for the whole day. Across complete cycles, every selected interest gets a turn. The old singular category parameter has been replaced and returns a descriptive 400.

The five photography themes resolve a new source-checked image each UTC day. The six WallCab Original SVG themes remain visually constant; only the lesson changes each day.

When background is present, the private custom image overrides the selected theme while the lesson still changes every day. The theme remains in the URL as a safe fallback if the upload is deleted, expires, or is temporarily unavailable. Existing URLs without background work exactly as before.

When note is present, WallCab replaces the lower fact area with a PERSONAL NOTE section. A blank or omitted value removes the section completely. The note is part of the URL, so do not put passwords, addresses, phone numbers, or other sensitive information there.

Successful response

A cache miss returns 200 image/png. A cache hit may return a short-lived 307 redirect to a signed Worker asset. Shortcut and browser clients follow this redirect automatically. A request with note always returns the generated image directly and reports BYPASS; personalized final PNGs are not stored in the shared Worker image cache.

Useful response headers:

HeaderMeaning
ETagStable hash of the generated image
X-WallCab-DateUTC content date
X-WallCab-CategoriesNormalized interests supplied by the user
X-WallCab-CategoryResolved learning category
X-WallCab-Content-Modeexternal or fallback
X-WallCab-Content-ProviderProvider used for the accepted lesson
X-WallCab-Background-Modecustom, daily-photo, or fixed-design
X-WallCab-ThemeResolved visual theme
X-WallCab-SizeResolved device preset
X-WallCab-CacheHIT, MISS, or BYPASS for a personalized image
X-Request-IdIdentifier for operational debugging

Invalid input

Unsupported parameters return 400 application/json and list every allowed option.

{
  "code": "INVALID_WALLPAPER_OPTIONS",
  "message": "Choose supported categories, a theme, a size, and an optional note of no more than 80 characters.",
  "allowed": {
    "categories": ["vocabulary", "coding"],
    "theme": ["nature", "mountains"],
    "size": ["standard", "air", "max"]
  },
  "requestId": "..."
}

The arrays in the real response are complete; they are shortened above for readability.

Rate limits and failures

The application currently allows 60 requests per client per minute. A 429 response includes Retry-After and X-RateLimit-* headers. Generation failures return 502 with the code WALLPAPER_GENERATION_FAILED.

Source status

Use the same query with /api/wallpaper/status to see the category selected today and whether the external provider or reviewed fallback supplied its lesson:

GET /api/wallpaper/status?categories=coding,science,history&theme=space&size=max
{
  "date": "2026-07-25",
  "selectedCategories": ["coding", "science", "history"],
  "resolvedCategory": "science",
  "content": {
    "mode": "external",
    "provider": "Wikimedia",
    "source": {
      "label": "Wikipedia",
      "url": "https://en.wikipedia.org/wiki/Example",
      "license": "CC BY-SA 4.0"
    }
  },
  "background": {
    "mode": "daily-photo",
    "theme": "space"
  },
  "requestId": "..."
}

The status lookup and image renderer share the same active-day KV lesson. This avoids extra provider calls and ensures the reported source matches the wallpaper.

Production logs

In Vercel, open the WallCab project, choose Logs, and search for wallcab.wallpaper. Each wallpaper request writes one structured entry with contentMode (external or fallback), contentProvider, fallbackReason, the resolved category, and cache status. Search for wallcab.content to see the original daily provider decision when a lesson is first accepted.

No client IP address is written into these application log entries. The application also does not copy note into its structured log entry. However, hosting infrastructure may log the full requested URL, so notes should never contain sensitive information.

Custom-background upload

The homepage is the recommended client. It resizes a selected JPEG, PNG, or WebP before sending image, turnstileToken, and the explicit rightsConfirmed=true declaration as multipart/form-data:

POST /api/custom-backgrounds

The prepared file must be at most 4 MiB and at least 600×600 pixels. WallCab validates the real image format, removes metadata, rotates it correctly, normalizes it to a portrait WebP, and stores it privately in Workers KV. The successful response contains an opaque backgroundId and a private deletionUrl. It contains no public raw-image URL.

Keep the deletion link private. Its secret is stored after #, so it is not sent in normal page requests or referrer headers. Deletion requires an explicit confirmation on that page and calls:

DELETE /api/custom-backgrounds/{backgroundId}
Content-Type: application/json

{"deleteToken":"..."}

An upload is removed after 30 days without a wallpaper read. Deletion purges the source and active generated variants; a previously issued signed cache URL can remain usable for at most its five-minute lifetime.

Examples

Browser:

https://your-domain.example/api/wallpaper?categories=science,history&theme=space&size=max

With a personal note:

https://your-domain.example/api/wallpaper?categories=vocabulary&theme=minimal&size=standard&note=Property%20of%20Dhruv

cURL:

curl --fail --location \
  "https://your-domain.example/api/wallpaper?categories=history,psychology&theme=mountains&size=standard" \
  --output wallcab.png

Metadata only:

curl --head --location \
  "https://your-domain.example/api/wallpaper?categories=finance,productivity&theme=minimal&size=air"