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 |