# Uselss Store publishing API Uselss Store lets a signed-in Google member publish and update only that member's apps through a personal API key. ## Safe setup The human creates a named key at /settings/api-keys. The secret is shown once. Store it in USELSS_API_KEY in the environment. Do not ask a human to paste an API key into an agent prompt, chat, manifest, source file, or commit. Set USELSS_STORE_URL to this explicit site origin: https://maximumeffort.lol. HTTPS is required except for a loopback development origin. Use no redirects or embedded credentials. The CLI needs Node.js 22 or newer. Download https://maximumeffort.lol/cli/uselss.mjs and https://maximumeffort.lol/cli/app.example.json. Run: node uselss.mjs publish app.json --image cover.png node uselss.mjs publish app.json --dry-run node uselss.mjs get my-stable-external-id node uselss.mjs unpublish my-stable-external-id node uselss.mjs republish my-stable-external-id node uselss.mjs delete my-stable-external-id --confirm When instructing Claude or Codex, use this compact prompt: Read https://maximumeffort.lol/llms.txt and publish this app with the local Uselss Store CLI. Use USELSS_STORE_URL and USELSS_API_KEY already present in the environment; do not request, print, persist, or paste either value. Use a stable externalId, include links: {} if there are no links, and run --dry-run before publishing. ## API Send Authorization: Bearer $USELSS_API_KEY. PUT /api/v1/apps/{externalId} uses multipart/form-data with app as JSON and image as an optional PNG, JPEG, or WebP file (maximum 5 MiB). The total request limit is 6 MiB. Image is mandatory for a first publish and optional when updating. app fields are name, tagline, description, optional story, platform, version, and links. The HTTP API accepts omitted links; the CLI manifest requires links: {} when no links are supplied. externalId belongs in the URL and must match [A-Za-z0-9][A-Za-z0-9._-]{0,79}. Reuse one externalId for the same app. This updates it safely and preserves its slug. GET /api/v1/apps/{externalId} reads that owner's app and its status: published, unpublished, deleted, hidden, or removed. PUT edits an unpublished app without publishing it. Use PATCH with application/json body {"action":"unpublish"} or {"action":"republish"} to change publication state. Moderated hidden or removed apps cannot be republished. DELETE permanently removes app content, reactions, and image references, then retains a minimal tombstone; the externalId can never be reused or resurrected. Repeating lifecycle actions is safe. Unpublishing, republishing, and content updates preserve the app's original publication timestamp and reaction records, including their original timestamps. Deletion is permanent and removes that content and reaction history. Images must be static, at most 16,777,216 pixels and 8192 pixels per dimension. The server re-encodes them to metadata-free WebP, at most 1920 x 1920 and 2 MiB. Upload limits: 5/minute and 50/rolling 24 hours per account; 100 MiB image storage and 100 apps per account. Failed admitted uploads count. Errors: 429 upload_rate_limited or image_processing_busy, 403 storage_quota_exceeded, 503 store_capacity_reached. Store executable installers on GitHub Releases; submit links rather than binaries. Publish success returns {"app":{"id":"uuid","externalId":"...","slug":"...","url":"https://..."},"created":true|false}. Lifecycle success returns the same app reference with "status". Failure responses are {"error":{"code":"...","message":"..."}}. Statuses: 400 malformed input or unsupported image, 401 invalid/revoked/expired key, 403 forbidden or moderated state, 404 app missing, 410 deleted tombstone, 413 size limit, 415 wrong content type, 429 rate limit, 503 temporary unavailable. Keys expire after 90 days; an account can have 10 active keys and 60 requests per minute. ## curl Key creation is limited to 20 per account in a rolling 24 hours. Settings lists all active keys and up to 100 recent key records. A website URL can belong to one app; 400 website_already_used requires updating that app or supplying a different URL. curl --fail-with-body -X PUT "$USELSS_STORE_URL/api/v1/apps/mood-clock-v1" \ -H "Authorization: Bearer $USELSS_API_KEY" \ -F 'app={"name":"Mood Clock","tagline":"Time, but moody.","description":"A small clock that changes its face based on a completely arbitrary feeling.","links":{"website":"https://example.com/mood-clock"}};type=application/json' \ -F "image=@cover.png;type=image/png" curl --fail-with-body -X PATCH "$USELSS_STORE_URL/api/v1/apps/mood-clock-v1" \ -H "Authorization: Bearer $USELSS_API_KEY" \ -H "Content-Type: application/json" \ --data '{"action":"unpublish"}' curl --fail-with-body -X DELETE "$USELSS_STORE_URL/api/v1/apps/mood-clock-v1" \ -H "Authorization: Bearer $USELSS_API_KEY"