Docs

Velven for agents

Velven is the community leaderboard for spaces built with AI: games, toys, tools, simulations, art. Spaces stay where they are hosted; Velven lists them, counts plays and ranks them. This page is for coding agents that deployed a space and want to list it for the creator. The flow is a device-code login: no API key is ever copied by hand.

Plain text: /docs/agent.md

1. Ask for a login link

One unauthenticated call. agent_name is optional (max 60 characters) and is shown to the creator on the approval screen.

curl -s -X POST https://velven.ai/api/agent/login \
  -H "content-type: application/json" \
  -d '{"agent_name":"Claude Code"}'

Response, status 201:

{
  "device_code": "a3Vw…",
  "user_code": "K7PX-4MDQ",
  "verification_url": "https://velven.ai/agent/approve?code=K7PX-4MDQ",
  "expires_in": 900,
  "interval": 5
}

Keep device_code to yourself. Show the creator the verification_url and the user_code. A 429 rate_limited means this address opened too many logins in the last quarter hour; wait, do not retry in a loop.

2. The creator approves

Tell the creator: "Open this link and press Approve. The code on the page should read K7PX-4MDQ." If they are not signed in to Velven yet, the same screen signs them in with Google or a magic link and asks for a handle. Sign-up lives inside the approval.

Meanwhile poll for the token every interval seconds, and give up after expires_in seconds:

curl -s -X POST https://velven.ai/api/agent/token \
  -H "content-type: application/json" \
  -d '{"device_code":"<device_code>"}'
StatusBodyWhat to do
428{ "error": "authorization_pending", "interval": 5 }Not decided yet. Wait interval seconds, poll again.
200{ "access_token", "token_type": "bearer", "handle", "profile_url" }Approved. Store the token; it is returned exactly once and lasts 90 days. A 401 later means it expired or the creator revoked it: log in again.
403{ "error": "access_denied" }The creator pressed Deny. Stop and say so.
400{ "error": "expired_token" }15 minutes passed, or the code is unknown. Start over at step 1.
400{ "error": "invalid_grant" }This device code was already exchanged. Use the token you have, or start over.

Store the token at ~/.config/velven/token with mode 600 and reuse it for later spaces by the same creator. When any call answers 401, delete the file and log in again. Tokens are tied to one creator and only work on /api/spaces and /api/agent/me.

Check who a token belongs to:

curl -s https://velven.ai/api/agent/me -H "authorization: Bearer $VELVEN_TOKEN"
{ "handle": "mara", "profile_url": "https://velven.ai/@mara" }

3. Put the proof on the site

Only a verified creator can list a space, so the proof goes on the site before you submit. It names the creator's handle from the token response. You can do this part yourself on Vercel, Netlify and ChatGPT sites; a Claude artifact needs the creator.

HostProof
Vercel, Netlify, ChatGPT sites<meta name="velven" content="@handle"> inside the page's <head>. Deploy or publish again.
Claude artifactsIn Claude, Publish → Get embed code → Allowed domains: add handle.velven.ai next to velven.ai. Velven reads it from the artifact's frame-ancestors header.

Wait for the deploy to be live before submitting. If you submit too early the answer is a 409 with the exact change still needed.

4. Submit the space

Fill provenance in from the project rather than asking the creator: the engine from package.json and imports, the AI tools from yourself, devices from pointer and touch handling, the description from the README. Velven fetches the URL, follows redirects, checks the proof, decides whether it can play in place, and falls back to the page title, meta description and og:image for anything you leave out.

curl -s -X POST https://velven.ai/api/spaces \
  -H "authorization: Bearer $VELVEN_TOKEN" \
  -H "content-type: application/json" \
  -d @- <<'JSON'
{
  "url": "https://orbit-dodger.netlify.app",
  "title": "Orbit Dodger",
  "description": "Dodge debris in a decaying orbit. Arrow keys or swipe.",
  "space_type": "game",
  "engine": "three.js",
  "ai_tools": ["claude-code"],
  "devices": ["desktop", "mobile"],
  "how_made": "One session with Claude Code. Three.js scene, hand-rolled physics, procedural debris field.",
  "source_url": "https://github.com/mara/orbit-dodger",
  "screenshot_url": "https://orbit-dodger.netlify.app/og.png"
}
JSON
FieldRequiredNotes
urlyesPublic http(s) URL of the space. Stored after redirects.
titlenoMax 80. Falls back to og:title or <title>.
descriptionnoOne line, max 160. Falls back to the meta description.
space_typeyesOne of the values below.
engineyesOne of the values below.
ai_toolsyesArray, at least one value from below.
devicesyesArray, at least one value from below.
how_madenoMax 2000. Prompts, process, what was hard.
source_urlnoRepository or artifact link.
screenshot_urlnoAbsolute image URL. Falls back to og:image, then an automatic capture.

Response, status 201. The page is live on the creator's handle and on the leaderboard:

{
  "slug": "orbit-dodger",
  "url": "https://velven.ai/@mara/orbit-dodger",
  "embed_mode": "iframe",
  "nudge": null,
  "title": "Orbit Dodger"
}
StatusBodyMeaning
409{ "error": "unverified", "instruction", "snippet" }The proof is not on the live site yet. Make the change instruction names, deploy, wait for the cache, call again. Do not retry more than a few times without changing something.
401{ "error": "unauthorized" }Missing, revoked or unknown token. Log in again.
403{ "error": "blocked" }The URL or the account cannot list spaces.
409{ "error": "duplicate", "url": "…" }Already on Velven. url is the existing page. If it shows as unclaimed, Velven listed it itself; see below.
422{ "error": "invalid", "issues": [{ "path", "message" }] }Fix the listed fields and retry. A url issue also means the host is not one Velven lists yet.

5. Reply to the creator

Reply with the url. If nudge is set, pass it along verbatim; it explains why the space opens in a new tab and how to fix that. Offer the badge for the README:

<a href="https://velven.ai/@mara/orbit-dodger"><img src="https://velven.ai/badge/orbit-dodger" alt="On Velven"></a>

List what this creator has already listed:

curl -s "https://velven.ai/api/spaces?mine=1" -H "authorization: Bearer $VELVEN_TOKEN"
[{ "slug": "orbit-dodger", "title": "Orbit Dodger", "url": "https://orbit-dodger.netlify.app", "plays": 412, "upvotes": 18, "velven_url": "https://velven.ai/@mara/orbit-dodger" }]

Claiming a space Velven listed

Velven seeds the board with a few curated spaces of its own. Those show as unclaimed at /s/slug, and a duplicate error on submit points at one. With the same proof on the site, claim it for the creator; its plays stay and it moves to their handle.

curl -s -X POST https://velven.ai/api/spaces/verify \
  -H "authorization: Bearer $VELVEN_TOKEN" \
  -H "content-type: application/json" \
  -d '{ "slug": "orbit-dodger" }'
StatusBodyMeaning
200{ "verified": true, "url": "…" }Done. url is now the creator's page, /@handle/slug.
409{ "error": "unverified", "instruction", "snippet" }The proof is not on the live site yet. Deploy, wait, call again.
409{ "error": "owned" }Someone else already verified it. Tell the creator; they can report it from the page.
404{ "error": "not_found" }No such slug.
403{ "error": "blocked" }This account cannot claim spaces. Tell the creator, stop.
422{ "error": "invalid", "issues" }The body was not { slug }.

Allowed values

space_type

  • game Game
  • toy Toy
  • tool Tool
  • simulation Simulation
  • art Art

engine

  • three.js Three.js
  • r3f React Three Fiber
  • babylon.js Babylon.js
  • playcanvas PlayCanvas
  • a-frame A-Frame
  • godot Godot web
  • unity Unity web
  • phaser Phaser
  • p5.js p5.js
  • canvas Canvas / vanilla JS
  • marble Marble
  • spline Spline
  • other Other

ai_tools

  • claude-code Claude Code
  • claude Claude
  • cursor Cursor
  • codex Codex
  • copilot GitHub Copilot
  • gemini Gemini
  • windsurf Windsurf
  • lovable Lovable
  • bolt Bolt
  • v0 v0
  • replit Replit
  • marble Marble (world model)
  • other Other

devices

  • desktop Desktop
  • mobile Mobile
  • vr VR headset

Hosting

Velven lists spaces hosted on Vercel, Netlify, ChatGPT sites and Claude artifacts for now; custom domains on Vercel and Netlify are recognised by their response headers. Other hosts are refused with 422. Vercel, Netlify and ChatGPT sites allow framing, so the space plays in place (embed_mode: "iframe"). A Claude artifact plays in place once the creator publishes it and adds the Velven domain under "Get embed code" → Allowed domains; otherwise it opens in a new tab (embed_mode: "newtab") and plays still count.

Velven checks each host's framing headers at submit time and again in the background, so a space can move from new tab to in place later without resubmitting.

Skill and plain-text docs

Agents that use skills.sh can install the Velven skill with npx skills add velvenai/app. The skill lives at skills/velven/SKILL.md in the Velven repository and teaches this whole flow.

This page as markdown: https://velven.ai/docs/agent.md. Index for crawlers and agents: https://velven.ai/llms.txt.