Skip to main content

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.json in the UI, artifact limit 2 GB, runs an automated static scanner at upload time. See Submission Guide.
Both end in the same place: a human reviews the release before its catalog status can become 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_filter covering infrastructure and local stubs (sdk/*, plus core/*,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
Export with the same Summer Engine executable used for local testing (no export templates are required for a pack export):
Checks after export:
  • manifest.json is 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.
Then measure what you will declare:

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.
A present-but-invalid Bearer token is a hard 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. A 429 body tells you when to come back:
Validate everything locally before touching these endpoints.

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:
  • name required, 1–80 chars. slug optional: explicit slugs collide loudly (409 slug_taken); omitted, the server derives one from the name and walks around collisions.
Success 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, no sha256: prefix.
  • sizeBytes: positive integer, 1024–536870912.
  • contentType optional; if sent it must be "application/octet-stream".
Success 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 answers 412 Precondition Failed.
Errors: 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

Success is an empty 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

Nothing you claim is trusted: the server checks its own presign-time intent record, HEADs the stored object, streams it through sha256, and compares digest and size against the declaration. Only then does a release row exist — and it is immutable from that moment. Success 201:
The release is 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

  1. Finalize → release pending_review; the admin queue is pinged.
  2. 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 published catalog status. Rejection records a reason, shown to you on summercraft.ai/creator; the previously approved catalog release stays active.
  3. There is no automated publish, no runtime sandbox yet, and no guaranteed review time.
What approval does not (yet) mean: browser play, desktop shell play, and hosted game servers for uploaded Summer games are not live. Approved releases are distributed through the authenticated download endpoint; play surfaces are in development. The canonical table is platform capability status.

10) Updating your game

Releases are immutable — updating means a new one:
  1. Keep the same gameId (and manifest.id).
  2. Bump manifest.version, and use that same new string as version in upload-url and finalize.
  3. Re-export, then repeat steps 5–7.
Each update goes through review again.

Previous: Test locally

Return to the Summer Engine validation loop.

Next: Submit and review

Choose a submission path and understand review states.