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.
Streaming upload
A session transcript can be larger than the 100 MB body limit — and larger than
the server can hold in memory at once. For those, send the same request as
Content-Type: application/x-ndjson instead: the metadata object goes on the
first line, with data omitted, and the session records follow it one per line.
POST /api/v1/sessions
Content-Type: application/x-ndjson
Content-Encoding: gzip
{"session_id":"<local UUID>","provider":"claude","title":"optional title"}
{"type":"user","uuid":"...","message":{...}}
{"type":"assistant","uuid":"...","message":{...}}
Content-Encoding: gzip is optional but recommended: the limit that matters is
measured on the compressed bytes as they arrive, so compressing the upload is
what makes a transcript well past 100 MB pushable. The server never buffers the
records — it hashes, compresses and stores them as they stream past — so the
practical ceiling is the compressed request size, not the transcript size.
The response is the same NDJSON event stream, and the stored result is identical
to the JSON framing. Empty lines are dropped and every record is stored newline
terminated, so the size the server reports may differ slightly from the file on
disk. rn uses this framing for every session push.
Errors before the stream starts are ordinary JSON responses: 413 if the body
exceeds the limit, 415 for a Content-Encoding other than gzip, 400 if the
metadata line is not a JSON object.
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 |