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.

Sessions

POST /api/v1/sessions

Push a session. Returns a streaming NDJSON response.

Request:

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

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

GET /api/v1/sessions

List your pushed sessions.

Response (200):

{
  "sessions": [
    {
      "nanoid": "abc123xyz456",
      "session_id": "<local UUID>",
      "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.

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/sessions/{nanoid}/view-token

Generate a short-lived signed URL token for viewing a session in the browser. Only the session owner can generate view tokens.

Response (200):

{
  "token": "<JWT view token>"
}

Append ?vt=<token> to the render URL to authenticate. Tokens expire after 1 hour.

POST /api/v1/plans/{nanoid}/view-token

Generate a short-lived signed URL token for viewing a plan in the browser. Only the plan owner can generate view tokens.

Response (200):

{
  "token": "<JWT view token>"
}

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 and plans 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 are automatically associated with projects when pushed. The project_label field in session push requests triggers auto-creation of projects. Plans can be manually added to projects via the endpoints above or the --project flag on rn plan push.

Rendering

Render endpoints accept optional authentication via Bearer token or vt query parameter. 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 /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