# ScratchSpace — agent guide https://scratchspace.dev is a project directory; https://pages.scratchspace.dev is a static-page publishing service with hierarchical folders. Agents manage both through one JSON API: https://scratchspace.dev/api/v1 ## Authentication Every request needs a bearer token: Authorization: Bearer ssp_... Tokens are created by a human at https://scratchspace.dev/admin/tokens and carry scopes: projects:read, projects:write, pages:read, pages:write. If you are running on a machine where the ScratchSpace CLI is set up, the token is usually at ~/.config/scratchspace/token. Errors return JSON: {"error": "message"}. ## Publishing a page (the common task) A page is a directory containing index.html plus any relative assets (e.g. assets/photo.jpg). Publishing is atomic in three steps: declare a manifest, upload every file, commit. Nothing is reachable until commit, and replacing an existing page keeps the old version live until commit. Choose the visibility with the user and always submit it explicitly: - "PUBLIC" means anyone with the direct URL can load the page. - "PRIVATE" means only administrators and agents with pages:read can load it. Folders have the same choices, and a PRIVATE folder makes its entire subtree private even when a child page or folder is marked PUBLIC. Directory listings are admin-only; visibility never makes a directory publicly browsable. 1. Start a publication. Compute size and SHA-256 (lowercase hex) of every file first: curl -X POST https://scratchspace.dev/api/v1/pages/publications \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "path": "travel/my-trip", "title": "My Trip", "createFolders": true, "visibility": "PUBLIC", "files": [ {"path": "index.html", "size": 1234, "sha256": "<64 hex>"}, {"path": "assets/photo.jpg", "size": 56789, "sha256": "<64 hex>"} ] }' Response: {"publicationId": "...", ...} 2. Upload each file's raw bytes (any order; re-upload allowed while staged; the server verifies size and hash against the manifest): curl -X PUT "https://scratchspace.dev/api/v1/pages/publications/$ID/files/index.html" \ -H "Authorization: Bearer $TOKEN" --data-binary @index.html curl -X PUT "https://scratchspace.dev/api/v1/pages/publications/$ID/files/assets/photo.jpg" \ -H "Authorization: Bearer $TOKEN" --data-binary @assets/photo.jpg 3. Commit (refused with 409 until every manifest file is uploaded): curl -X POST "https://scratchspace.dev/api/v1/pages/publications/$ID/commit" \ -H "Authorization: Bearer $TOKEN" The page is then live at https://pages.scratchspace.dev/travel/my-trip/ (canonical URLs end with a slash; relative asset paths inside your HTML resolve naturally). Inspect progress: GET /api/v1/pages/publications/$ID Abort while staged: DELETE /api/v1/pages/publications/$ID ## Rules and limits - Site paths ("travel/my-trip") are lowercase segments matching [a-z0-9][a-z0-9._-]*, max 80 chars each, max depth 8. Reserved top-level names: api, admin, robots.txt, sitemap.xml, favicon.ico, favicon.svg. - The manifest must include index.html at the bundle root (exact case). - Bundle file paths are relative, no leading dot or traversal, and must have a known static extension (html, css, js, json, svg, png, jpg, gif, webp, avif, ico, woff2, mp3, mp4, webm, pdf, wasm, ...). - Limits: 500 files, 25 MiB per file, 200 MiB per bundle. - Replaced pages keep their previous version recoverable for 30 days. ## Other endpoints (scope in parentheses) Folders and pages: - GET /api/v1/pages/folders?path=

list a folder; "" = root (pages:read) - POST /api/v1/pages/folders {path, title?, createParents?, visibility: "PUBLIC"|"PRIVATE"} (pages:write) - PATCH /api/v1/pages/folders {path, title?, visibility?} (pages:write) - DELETE /api/v1/pages/folders {path} — 409 if non-empty; recursive delete requires {recursive: true, confirm: ""} (pages:write) - GET /api/v1/pages/page?path=

page details + versions (pages:read) - PATCH /api/v1/pages/page {path, title?, visibility?} (pages:write) - DELETE /api/v1/pages/page {path} (pages:write) GET responses include visibility. For direct reads of a PRIVATE published page on https://pages.scratchspace.dev, send the same Authorization: Bearer header; pages:read is required. Anonymous private, directory, and nonexistent paths all return the same 404 response. Project directory (the https://scratchspace.dev landing page): - GET /api/v1/projects all projects (projects:read) - POST /api/v1/projects {name, url, description, githubUrl?, visibility: "PUBLIC"|"PRIVATE"} (projects:write) - GET/PATCH/DELETE /api/v1/projects/:id (projects:read / projects:write) - POST /api/v1/projects/:id/reorder {direction: "up"|"down"} (projects:write) ## CLI alternative If you have the repository checkout, scripts/scratchspace.mjs wraps the whole publish flow: export SCRATCHSPACE_API_ORIGIN=https://scratchspace.dev node scripts/scratchspace.mjs pages publish

--path travel/my-trip --title "My Trip" --create-folders It reads the token from SCRATCHSPACE_TOKEN or ~/.config/scratchspace/token. The CLI currently creates PUBLIC pages and preserves an existing page's visibility when replacing it; use the API flow above to create a PRIVATE page.