# Agent publishing HTTP reference

Send requests to `https://api.rosebud.ai`. The [Rosebud CLI](https://www.npmjs.com/package/@rosebudai/cli) is the supported client, and these endpoints may still change. HTTPS is required for credentials outside local development. All connection and mutation responses are non-cacheable. No generative-model calls run on import, claim, approval or update.

## Import a new project

`POST /api/developer/projects/import/` — anonymous multipart:

| Field         | Behavior                                                                                                                                    |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `bundle`      | One browser-ready ZIP, root index.html; 10 MiB compressed, 50 MiB expanded, 500 entries including directories, subject to configured bounds |
| `title`       | Optional 1–255 character title; legacy `name` alias is accepted if consistent                                                               |
| `description` | Optional plain text, up to 4000 characters                                                                                                  |
| `thumbnail`   | Optional separate PNG/JPEG file, at most 2 MiB and 4096 pixels per side; decoded/re-encoded to strip metadata                               |
| `context`     | Optional JSON string with the self-reported fields below; private to Rosebud                                                                |

Every field except `bundle` is optional. Without a title the game is called “Uploaded game” and gets a generic description, so pass them when you know them. No screenshot or model call is run for the cover.

`context` accepts only these fields, all optional. The CLI adds its own version and interface. Keep prompts, conversations, personal details, local paths and environment variables out of it.

| Field              | Values                                                                              |
| ------------------ | ----------------------------------------------------------------------------------- |
| `agent_client`     | codex, claude-code, gemini-cli, cursor, other, unknown                              |
| `agent_version`    | Reported version, at most 80 characters                                             |
| `model_provider`   | openai, anthropic, google, other, unknown                                           |
| `model`            | Reported model name, at most 100 characters                                         |
| `use_case`         | learning, game-jam, prototype, portfolio, classroom, client-work, commercial, other |
| `framework`        | vanilla, phaser, three, other, unknown                                              |
| `discovery_source` | docs, search, github, recommendation, existing-user, other, unknown                 |

Success HTTP 201: `project_id`, public `play_url`, **private** `claim_url`, `claim_expires_at` (UTC), and `content_version`. Unclaimed games expire after one hour. Claim before `claim_expires_at` to keep the same link online. The public game, code and asset endpoints return HTTP 410 `game_expired` at the deadline. Background cleanup permanently deletes the unclaimed project, code, unshared assets and cover; deleted resources return HTTP 404. There is no late claim or recovery. Every import creates a new game. A fresh retry after a lost response can create another one.

## Request an agent connection

`POST /api/developer/projects/<project_id>/connect/` — anonymous JSON:

```json
{ "claim_secret": "<fragment of the initial claim_url>", "client": "codex" }
```

For an already claimed project, omit `claim_secret`; only its signed-in owner can approve the request. For an unclaimed game, its correct private claim secret is required. The public project ID alone never grants ownership.

Success HTTP 201: `project_id`, `connection_id`, private `agent_token`, private `approval_url`, `status: "pending"`, `expires_at`. Persist the agent token securely and show only the approval URL to the person. They are different secrets; neither is a Rosebud account JWT. Pending requests expire in 30 minutes.

The person opens the approval link, signs in, reviews the game and requesting client, then explicitly approves. Approved access expires in 90 days. Client names are self-reported. Browser inspection uses authenticated `POST /api/developer/project-connections/inspect/` with `{ "secret": "<approval fragment>" }`; approval uses the same body at `/approve/`. Inspection and GET visits never claim or authorize. Approval can atomically claim an unclaimed import and activate its grant for that account.

## Check, update and disconnect

`GET /api/developer/projects/<project_id>/connection/` with `Authorization: Bearer <agent_token>` returns `project_id`, `connection_id`, `status` (pending/active/revoked/expired), and `expires_at`. An active response also includes `title`, `play_url`, `content_version`.

`POST /api/developer/projects/<project_id>/releases/` uses that same header and multipart fields:

- `bundle`: complete replacement build, with the same import validation.
- `expected_version`: the last content version the caller explicitly accepted.
- `operation_id`: a UUID persisted before the request and retained for a retry.
- Optional `title`/`name`, `description`, `thumbnail`: omitted fields retain their existing values; an explicit empty description clears it.

Success HTTP 200: `project_id`, stable `play_url`, new `content_version`, `operation_id`. The project ID and slug stay fixed even when its title changes. Complete current/published revisions commit together; failures preserve the earlier game. Approved credentials have no account-wide, generation, billing or paid-deployment scope.

A retry with the same operation ID and validated content/metadata returns the recorded result, without another release. Reuse with changed input is rejected. Browser code, media, publication and metadata changes invalidate stale versions. Fetch status, reconcile source and submit a new operation with the explicitly accepted current version. Do not automatically overwrite after a conflict. A replay reports the original operation's result even if a later browser change occurred; fetching status reports current state.

`DELETE /api/developer/projects/<project_id>/connection/` with the agent token revokes that credential. The owner can also list connections via authenticated `GET /api/developer/projects/<project_id>/connections/` and revoke one via `DELETE /api/developer/projects/<project_id>/connections/<connection_id>/`. The list returns up to 50 connections, with active grants first; pass its `next_offset` as `?offset=<value>` until that value is null. The UI is in Project Settings → Manage connected agents, at `/connect/projects/<project_id>`.

## Claim-only compatibility

Anonymous `POST /api/developer/project-claims/inspect/` with `{ "secret": "<claim fragment>" }` returns `project_id`, `title`, `status` (`pending`, `claimed`, `expired`) and `claim_expires_at` (null once claimed). It never transfers ownership.

The initial private claim link still supports a signed-in, explicit claim without connecting an agent. Authenticated `POST /api/developer/project-claims/` takes `{ "secret": "<claim fragment>" }`. It transfers the project once and returns `project_id`, `play_url`, `editor_url`. Visiting the link does not claim it. A consumed claim cannot transfer ownership again. Expired claims and approval attempts return HTTP 410 `game_expired` before cleanup, or HTTP 404 after deletion. Uploading a retained local build creates a new project and link. A successful claim before the deadline removes the game expiration, including when claiming as part of agent approval.

## Errors and correction

Domain failures return `{ "error": { "code", "message", "filename"?, "limit"? } }`; request serializer failures use DRF field errors. Common codes:

| Code                                                                                   | Action                                                                                                                                          |
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `missing_index`, `unsafe_path`, `unsupported_file`, `invalid_media`                    | Correct the bundle; keep browser-ready files and safe relative paths                                                                            |
| `media_path`, `noncanonical_path`                                                      | Move binary media under `assets/` and rename it to the lowercase path in the message, updating references                                       |
| `compressed_limit`, `expanded_limit`, `entry_limit`                                    | Reduce the indicated size/count; limit values describe actual configured bounds                                                                 |
| `thumbnail_limit`, `invalid_thumbnail`                                                 | Supply a valid bounded PNG/JPEG cover                                                                                                           |
| `game_expired`                                                                         | The one-hour claim deadline passed. Upload the local build as a new project with a fresh CLI state file, then claim it before its new deadline. |
| `claim_required`, `invalid_claim`, `used_claim`                                        | Use the original private handoff or the claimed owner's account                                                                                 |
| `invalid_connection`, `connection_pending`, `connection_expired`, `connection_revoked` | Check saved project/origin/token; request human approval or a new connection                                                                    |
| `owner_required`                                                                       | Sign in as the game's current owner                                                                                                             |
| `version_conflict`                                                                     | Reconcile local and remote changes before explicitly accepting the current version                                                              |
| `operation_reused`                                                                     | Retry the original input, or choose a new operation ID after reconciliation                                                                     |
| `project_busy`                                                                         | Wait for Rosebud generation to finish                                                                                                           |
| `update_failed`                                                                        | Retry the same operation and payload; it can safely recover a committed result                                                                  |

Keep claim links, approval links and access tokens out of public game files, repositories and logs. No endpoint accepts credentials in URL query strings. Successful publication still requires a real gameplay check.

Supported bundle assets: PNG/JPEG/WebP/SVG images, WAV/MP3/OGG audio, WOFF/WOFF2/TTF/OTF fonts, WASM, GLB/glTF models and binary model buffers (.bin). Code files remain HTML, JS, CSS and JSON. UTF-8 `.txt` notices (case-insensitive extension) and extensionless LICENSE, LICENCE, NOTICE, COPYING, COPYRIGHT and AUTHORS are also accepted as text, preserving their names, case and contents. Put media and binary assets under `assets/` using canonical lowercase paths (letters, digits, internal underscores and single hyphens; no extra dots in stems). The directory CLI preflight reports the same path errors as the server. Preserve relative references, including glTF `.bin` buffers. Raster images are limited to 10000 pixels per side. Thumbnails remain separate PNG/JPEG uploads.
