Goal
Produce a.pck that contains only your game content, then submit it for review. There are
two deployed submission paths:
- The release API (this page, recommended — what agents and CI use): four HTTP calls against
https://summercraft.ai, artifact limit 512 MiB. - The browser submit page at summercraft.ai/submit: upload
.pck+manifest.jsonin the UI, artifact limit 2 GB, runs an automated static scanner at upload time. See Submission Guide.
published. No auto-publish, and no player-facing playback yet.
Working agent-first? /agent-setup has a paste-prompt that walks any coding agent through this entire page, including a verified project template.
1) Export a game-only .pck
Use a preset that ships your game and nothing else:- name:
Summer Game PCK export_filter="all_resources"include_filter="manifest.json"(non-resource files must be listed here)exclude_filtercovering infrastructure and local stubs (sdk/*, pluscore/*,server/*,client/*if you develop inside the Summer Engine starter template)script_export_mode=0— ship GDScript as readable text so review can read your source
manifest.jsonis at the pack root and current (reference),- the entry scene’s root script
extends SummerGame, - no reserved paths or banned APIs in the pack,
- size between 1024 bytes and 536870912 bytes (512 MiB) for the API path.
2) Authentication
Every endpoint accepts two forms, Bearer first:Authorization: Bearer <Supabase access token>— what agents, CI, and native clients use. See /agent-setup for how a signed-in human extracts their token.- The web session cookie — what the browser uses.
401; it never falls through to a cookie session. Tokens expire after about an hour.
3) Rate limits — read before calling
Create-game, upload-url, and finalize each have their own budget of 1 request per hour per account (window and count are policy values and may widen). Order of checks is deliberate: validation and ownership run first, so a typo never spends your budget — but a successful call does. A429 body tells you when to come back:
4) POST /api/games/engine — create the game record
Once per game, not per version. Creates a Summer game record (kind: "engine") as
metadata; the artifact arrives through the release endpoints.
Request:
namerequired, 1–80 chars.slugoptional: explicit slugs collide loudly (409 slug_taken); omitted, the server derives one from the name and walks around collisions.
201:
uploadUrlEndpoint is included when the slug was derived.)
Errors: 401 sign_in_required · 400 invalid_request · 409 slug_taken · 429 rate_limited · 502 create_failed.
5) POST /api/games/{gameId}/releases/upload-url — mint the presigned PUT
Declare exactly what you measured:
version: 1–32 chars of[A-Za-z0-9._-], first char alphanumeric, no... Immutable once released.sha256: lowercase hex, nosha256:prefix.sizeBytes: positive integer, 1024–536870912.contentTypeoptional; if sent it must be"application/octet-stream".
200:
- The URL expires after 1 hour.
- Both returned headers are folded into the URL’s signature. Send them verbatim on the PUT or storage answers
403 SignatureDoesNotMatch. if-none-match: *makes the object key write-once: a release artifact can never be swapped after it has been checksummed. A second PUT to the same key answers412 Precondition Failed.
401 sign_in_required · 400 invalid_request · 413 artifact_too_large · 404 game_not_found_or_forbidden (wrong id or not yours) · 409 not_a_godot_game (web games publish via their own flow) · 409 version_exists (bump the version) · 429 rate_limited · 503 r2_not_configured · 502 upload_url_failed.
6) PUT the artifact to uploadUrl
200. No Authorization header — the presigned URL is the credential. 412 means the key already holds an object (see write-once above); if that object is the same verified artifact, skip to finalize.
7) POST /api/games/{gameId}/releases/finalize — server-verified record
201:
pending_review: it is available only to the owner and admins until an
admin approves it and the game’s active version moves. Approval changes catalog/download
access; it does not make the game playable on the platform. On a checksum_mismatch or
size_mismatch the stored object is deleted (so the write-once key is free again for a
correct re-upload) and no row is written.
Errors: 401 sign_in_required · 400 invalid_request · 404 game_not_found_or_forbidden · 409 not_a_godot_game · 409 no_upload_intent (call upload-url first; declarations must match it exactly) · 409 artifact_missing (the PUT never landed) · 413 artifact_too_large · 400 size_mismatch · 400 checksum_mismatch · 409 version_exists · 429 rate_limited (its own budget) · 503 r2_not_configured · 502 finalize_failed.
Errors are self-describing — { "error", "detail" } where detail names the next action. Trust it.
8) GET /api/games/{gameId}/releases/{version}/download-url — verify what you shipped
Auth required, never anonymous. The game’s owner and admins can fetch any release, including pending ones; any other signed-in user only the approved release of a published game. Everything unauthorized answers 404 (not 403), so release existence cannot be probed.
Success 200:
url is a presigned GET valid for ~5 minutes; sha256 is read back from the content-addressed key so you can re-verify the bytes after download.
9) Review lifecycle
- Finalize → release
pending_review; the admin queue is pinged. - A human reviews. Approval re-verifies the stored bytes against the checksum pinned at
finalize, then points the game’s active version at the release and assigns
publishedcatalog status. Rejection records a reason, shown to you on summercraft.ai/creator; the previously approved catalog release stays active. - There is no automated publish, no runtime sandbox yet, and no guaranteed review time.
10) Updating your game
Releases are immutable — updating means a new one:- Keep the same
gameId(andmanifest.id). - Bump
manifest.version, and use that same new string asversionin upload-url and finalize. - Re-export, then repeat steps 5–7.
Previous: Test locally
Return to the Summer Engine validation loop.
Next: Submit and review
Choose a submission path and understand review states.

