API Endpoints

Authentication

POST /api/v1/account/register

Register a public key. Returns the key's fingerprint.

Request:

{
  "public_key": "<Ed25519 SSH public key in authorized_keys format>"
}

Response (201):

{
  "fingerprint": "SHA256:..."
}

Idempotent — registering the same key returns the existing fingerprint with status 200.

Max body: 8 KB.

POST /api/v1/account/challenge

Request a challenge nonce for authentication.

Request:

{
  "fingerprint": "SHA256:..."
}

Response (200):

{
  "challenge": "<base64-encoded nonce>",
  "expires_at": "2026-02-25T12:00:00Z"
}

The nonce is valid for 5 minutes.

POST /api/v1/account/verify

Verify a signed challenge and receive a JWT.

Request:

{
  "fingerprint": "SHA256:...",
  "signature": "<base64-encoded Ed25519 signature>"
}

The signature must be over the message rockstar.ninja.v1.auth:{nonce}:{fingerprint}.

Response (200):

{
  "token": "<JWT>",
  "expires_at": "2026-02-26T12:00:00Z"
}

Account Management

All account management endpoints require authentication.

GET /api/v1/account/me

Get current user info.

Response (200):

{
  "fingerprint": "SHA256:...",
  "username": "myname",
  "profile_url": "https://rockstar.ninja/@myname"
}

username and profile_url are omitted if no username is set.

POST /api/v1/account/username

Set or change your username.

Request:

{
  "username": "myname"
}

Response (200):

{
  "username": "myname",
  "profile_url": "https://rockstar.ninja/@myname",
  "warning": "next change available after 2027-02-25"
}

Returns 409 if the username is taken, 429 if you're in the cooldown period.

DELETE /api/v1/account/username

Release your username. Returns 204.

DELETE /api/v1/account/me

Permanently destroy your account. Requires confirmation.

Request:

{
  "confirm": "DESTROY"
}

Returns 204. Deletes all sessions, plans, username, and key registration.

Data Export

All data export endpoints require authentication.

POST /api/v1/account/export

Request a new data export. Replaces any existing export for the user. The archive is built asynchronously in the background.

Request:

{
  "format": "zip"
}

format is optional (default: zip).

Response (202):

{
  "token": "...",
  "status": "pending",
  "format": "zip",
  "requested_at": "2026-03-12T12:00:00Z",
  "expires_at": "2026-03-14T12:00:00Z"
}

Only one export per user at a time. Exports expire after 48 hours.

GET /api/v1/account/export

Get the current export status.

Response (200):

{
  "token": "...",
  "status": "ready",
  "format": "zip",
  "file_size": 123456,
  "session_count": 10,
  "plan_count": 5,
  "requested_at": "2026-03-12T12:00:00Z",
  "completed_at": "2026-03-12T12:01:00Z",
  "expires_at": "2026-03-14T12:00:00Z",
  "download_url": "https://rockstar.ninja/api/v1/account/export/download/{token}"
}

Status values: pending, building, ready, failed. file_size, session_count, plan_count, completed_at, and download_url are only present when status is ready. Returns 404 if no export exists.

DELETE /api/v1/account/export

Delete the current export (file and metadata). Returns 204. Returns 404 if no export exists.

GET /api/v1/account/export/download/{token}

Download the export archive. No authentication required — the token in the URL acts as a capability. Returns the ZIP file with Content-Type: application/zip. Returns 404 if the token is invalid, 410 if expired.

Sessions

POST /api/v1/sessions

Push a session. Returns a streaming NDJSON response.

Request:

{
  "session_id": "<local UUID>",
  "provider": "claude",
  "title": "optional title",
  "data": "<raw JSONL session content>",
  "privacy": "public",
  "expires_at": "7d"
}

Max body: 100 MB. provider defaults to claude. privacy defaults to secret (valid values: public, secret, private). expires_at accepts durations (24h, 7d) or ISO 8601 dates.

If the session contains plan file writes (~/.claude/plans/*.md), the streaming done event includes a plans array with the created or updated plans. See Streaming for details.

GET /api/v1/sessions

List your pushed sessions.

Response (200):

{
  "sessions": [
    {
      "nanoid": "abc123xyz456",
      "session_id": "<local UUID>",
      "provider": "claude",
      "slug": "auto-generated-slug",
      "title": "my session title",
      "privacy": "secret",
      "expires_at": "2026-03-04T12:00:00Z",
      "versions": 3,
      "url": "https://rockstar.ninja/s/abc123xyz456",
      "created_at": "2026-02-25 12:00:00",
      "updated_at": "2026-02-25 14:30:00"
    }
  ]
}

GET /api/v1/sessions/resolve?q={query}

Resolve an identifier to a nanoid. Resolution priority: exact nanoid, exact session_id, exact slug, session_id prefix.

Response (200):

{
  "nanoid": "abc123xyz456",
  "matched_by": "session_id"
}

Returns 409 with a matches array if the identifier is ambiguous (for example when the same session_id exists under different providers). Each match includes provider.

DELETE /api/v1/sessions/{nanoid}

Delete a session and all its versions. Returns 204.

DELETE /api/v1/sessions/{nanoid}/versions/{version}

Delete a specific version. Returns 204.

PATCH /api/v1/sessions/{nanoid}

Update session metadata. All fields are optional.

Request:

{
  "title": "new title",
  "privacy": "public",
  "expires_at": "30d"
}

Max body: 64 KB.

Plans

POST /api/v1/plans

Push a plan. Returns a streaming NDJSON response.

Request:

{
  "name": "my-plan.md",
  "title": "optional title",
  "content": "<markdown content>",
  "privacy": "public",
  "expires_at": "30d"
}

Max body: 10 MB. The name must end with .md. If the content hasn't changed since the last push, no new version is created.

GET /api/v1/plans

List your pushed plans.

Response (200):

{
  "plans": [
    {
      "nanoid": "xyz789abc012",
      "name": "my-plan.md",
      "title": "My Plan",
      "privacy": "public",
      "expires_at": null,
      "versions": 2,
      "url": "https://rockstar.ninja/p/xyz789abc012",
      "created_at": "2026-02-25 12:00:00",
      "updated_at": "2026-02-25 14:30:00"
    }
  ]
}

GET /api/v1/plans/resolve?q={query}

Resolve an identifier to a nanoid. Matches exact nanoid or plan name (with or without .md).

DELETE /api/v1/plans/{nanoid}

Delete a plan and all its versions. Returns 204.

PATCH /api/v1/plans/{nanoid}

Update plan metadata. Same fields as session PATCH. Max body: 64 KB.

POST /api/v1/account/web-login

Generate a one-time login code for web browser sessions. Requires Bearer JWT auth (CLI or macOS app). The code can be exchanged at GET /login?code=<code> to create a web session cookie.

Response (200):

{
  "code": "<one-time login code>"
}

The CLI's rn login command calls this endpoint and opens the browser to /login?code=<code> automatically. Login codes expire after 5 minutes.

Skills

POST /api/v1/skills

Push a skill. Returns a streaming NDJSON response.

Request:

{
  "name": "my-skill",
  "title": "optional title",
  "description": "skill description",
  "argument_hint": "optional argument hint",
  "content": "<SKILL.md markdown content>",
  "files": [
    {"path": "helper.py", "content": "<base64-encoded file content>"}
  ],
  "privacy": "public",
  "expires_at": "30d",
  "project_label": "my-project"
}

Max body: 50 MB. files contains all skill files with base64-encoded content. If the SKILL.md content hasn't changed since the last push, no new version is created — metadata updates (title, description, privacy, expiry) are applied without incrementing the version. Add ?force=1 to create a new version regardless.

GET /api/v1/skills

List your pushed skills. Requires authentication.

Response (200):

{
  "skills": [
    {
      "nanoid": "abc123xyz456",
      "name": "my-skill",
      "title": "My Skill",
      "description": "Does something useful",
      "argument_hint": "<filename>",
      "privacy": "public",
      "expires_at": null,
      "versions": 3,
      "file_count": 4,
      "total_size": 12345,
      "url": "https://rockstar.ninja/skill/abc123xyz456",
      "created_at": "2026-03-01 12:00:00",
      "updated_at": "2026-03-15 14:30:00",
      "project_nanoid": "pRj_Xm9kQ2w1",
      "project_label": "my-project"
    }
  ]
}

GET /api/v1/skills/resolve?name={name}

Resolve a skill name to a nanoid. Requires authentication (own skills only).

Response (200):

{
  "nanoid": "abc123xyz456",
  "matched_by": "name"
}

Returns 404 if not found.

GET /api/v1/skills/lookup

Public lookup of a skill by owner, name, and optional project. No authentication required — only returns public skills.

Query parameters:

Parameter Type Required Description
owner string yes Username of the skill owner
name string yes Skill name
project string no Project label (for disambiguation)

Response (200):

{
  "nanoid": "abc123xyz456",
  "matched_by": "lookup"
}

Returns 404 if not found or not public.

PATCH /api/v1/skills/{nanoid}

Update skill metadata. All fields are optional. Requires authentication as the owner.

Request:

{
  "title": "new title",
  "description": "new description",
  "argument_hint": "new hint",
  "privacy": "public",
  "expires_at": "30d"
}

Max body: 64 KB.

DELETE /api/v1/skills/{nanoid}

Delete a skill and all its versions and files. Returns 204.

GET /api/v1/skills/{nanoid}/download

Download a skill as a tar.gz archive. Optional authentication (public/secret accessible to anyone, private requires owner auth).

Query parameters:

Parameter Type Required Description
version int no Specific version to download (default: latest)

Response: application/gzip with Content-Disposition header.

GET /api/v1/skills/{nanoid}/files

List files in the latest version of a skill. Optional authentication.

Response (200):

[
  {"path": "SKILL.md", "size": 1234, "sha256": "abc..."},
  {"path": "helper.py", "size": 567, "sha256": "def..."}
]

GET /api/v1/skills/{nanoid}/files/{path}

Get the raw content of a single file. Optional authentication. Append ?html=1 for syntax-highlighted HTML output.

Projects

POST /api/v1/projects

Create a new project.

Request:

{
  "label": "my-project",
  "privacy": "secret",
  "description": "optional description"
}

Response (201):

{
  "nanoid": "pRj_Xm9kQ2w1",
  "label": "my-project",
  "privacy": "secret",
  "description": "",
  "url": "https://rockstar.ninja/proj/pRj_Xm9kQ2w1"
}

GET /api/v1/projects

List your projects.

Response (200):

{
  "projects": [
    {
      "nanoid": "pRj_Xm9kQ2w1",
      "label": "my-project",
      "privacy": "secret",
      "description": "",
      "sessions": 5,
      "plans": 2,
      "url": "https://rockstar.ninja/proj/pRj_Xm9kQ2w1",
      "created_at": "2026-02-25 12:00:00",
      "updated_at": "2026-03-01 14:30:00"
    }
  ]
}

GET /api/v1/projects/{nanoid}

Get a specific project.

PATCH /api/v1/projects/{nanoid}

Update project metadata (label, privacy, description). All fields are optional. Max body: 64 KB.

DELETE /api/v1/projects/{nanoid}

Delete a project. Sessions, plans, and skills within the project are kept but unlinked. Returns 204.

POST /api/v1/projects/{nanoid}/plans

Add an existing plan to a project.

Request:

{
  "plan_nanoid": "xyz789abc012"
}

DELETE /api/v1/projects/{nanoid}/plans/{planNanoid}

Remove a plan from a project (does not delete the plan).

GET /api/v1/projects/resolve?q={query}

Resolve a project identifier (nanoid or label) to a nanoid.

Note: Sessions, plans, and skills are automatically associated with projects when pushed. The project_label field in push requests triggers auto-creation of projects. Plans can also be manually added to projects via the endpoints above.

Search

GET /api/v1/search

Search across your sessions, plans, and skills. Requires authentication.

Query parameters:

Parameter Type Required Default Description
q string yes Search query
type string no Filter: session, plan, or skill
scope string no Content scope: user, assistant, agent, tool
project string no Filter by project nanoid
limit int no 20 Max results (1-100)
offset int no 0 Pagination offset

Response (200):

{
  "results": [
    {
      "type": "session",
      "nanoid": "abc123xyz456",
      "title": "fixing the auth bug",
      "snippet": "...updated the <mark>auth middleware</mark> to check...",
      "provider": "claude",
      "git_branch": "fix-auth",
      "updated_at": "2026-03-15 12:00:00"
    }
  ],
  "total": 42,
  "query": "auth middleware"
}

The snippet field contains HTML <mark> tags around matching terms. provider and git_branch are omitted when empty.

Rendering

Render endpoints accept optional authentication via Bearer token or web session cookie (rn_session). Public and secret content is accessible to anyone. Private content returns 404 unless the request is authenticated as the owner.

Endpoint Description
GET /s/{nanoid} Render session (latest version)
GET /s/{nanoid}/{version} Render session (specific version)
GET /s/{nanoid}/og.png Session social preview image
GET /p/{nanoid} Render plan (latest version)
GET /p/{nanoid}/{version} Render plan (specific version)
GET /p/{nanoid}/history Plan version history
GET /p/{nanoid}/diff/{v1}..{v2} Diff between two plan versions
GET /p/{nanoid}/{version}/{file} Raw plan markdown
GET /p/{nanoid}/og.png Plan social preview image
GET /skill/{nanoid} Render skill (latest version)
GET /skill/{nanoid}/{version} Render skill (specific version)
GET /skill/{nanoid}/history Skill version history
GET /skill/{nanoid}/diff/{v1}..{v2} Diff between two skill versions
GET /skill/{nanoid}/og.png Skill social preview image
GET /proj/{nanoid} Render project page
GET /proj/{nanoid}/og.png Project social preview image
GET /@{username} User profile page
GET /@{username}/og.png Profile social preview image
GET /@{username}/feed.xml Profile Atom feed
GET /dashboard Authenticated user dashboard with search
GET /login Login page; with ?code= exchanges code for session cookie
POST /logout Clear web session cookie