Files
llink/go/docs/api.md
T
2026-02-21 08:48:34 -08:00

16 KiB

API Documentation

All protected endpoints require a Bearer token in the Authorization header.

Authentication

Request Sign-In Code

POST /auth/request-code

Sends a 4-digit sign-in code to the provided email. Creates user if not exists.

Request Body:

{
  "email": "user@example.com"
}

Response: 204 No Content

Sign In

POST /auth/sign-in

Verifies the code and returns a session token.

Request Body:

{
  "email": "user@example.com",
  "code": "1234"
}

Response:

{
  "human": { "id": "...", "email": "...", "email_prefix": "...", "created_at": "..." },
  "token": "session_token"
}

Sign Out

POST /auth/sign-out (Protected)

Invalidates the current session. Returns 204 No Content.

Get Current User

GET /auth/me (Protected)

Returns the authenticated user.


Startup

Get Startup Data

GET /startup (Protected)

Bootstrap endpoint for initial app load. Returns all networks the user belongs to, with all streams and their particles fully enriched.

Response:

{
  "networks": [
    {
      "id": "net-456",
      "name": "My Team",
      "admin_human": { "id": "...", "email": "...", "email_prefix": "...", "created_at": "..." },
      "humans": [{ "id": "...", "email": "...", "email_prefix": "...", "created_at": "..." }],
      "open_stream_count": 2,
      "open_stream_capacity": 5,
      "created_at": "2025-01-15T10:30:00Z",
      "streams": [
        {
          "id": "p-001",
          "name": "Sprint Planning",
          "description": "Weekly sync",
          "status": "open",
          "members": ["alice@example.com"],
          "particles": [
            {
              "id": "p-002",
              "type": "text",
              "data": { "content": "Hello" },
              "created_by_email": "alice@example.com",
              "seen": true,
              "acks": [],
              "updated_at": "...",
              "created_at": "..."
            }
          ],
          "unseen_count": 0
        }
      ]
    }
  ]
}

Networks

Create Network

POST /networks (Protected)

Request Body:

{
  "name": "My Network"
}

List Networks

GET /networks (Protected)

Returns all networks the user is a member of.

Get Network

GET /networks/{id} (Protected)

Returns a specific network by ID.

Add Members to Network

POST /networks/{id}/members (Protected)

Request Body:

{
  "email_addresses": ["user1@example.com", "user2@example.com"]
}

Remove Member from Network

DELETE /networks/{id}/members/{email} (Protected)

Removes a member by email from the network.

Set Open Stream Capacity

PUT /networks/{id}/capacity (Protected, Admin only)

Request Body:

{
  "capacity": 10
}

Streams

Streams are top-level particles of type stream. They have dedicated endpoints for creation and management, and contain child particles.

Create Stream

POST /networks/{network_id}/streams (Protected)

Request Body:

{
  "name": "Sprint Planning",
  "description": "Weekly sync",
  "visibility": "custom",
  "members": ["user@example.com"]
}
  • visibility: network_all (default) or custom
  • members is required when visibility is custom

Response: 201 Created — returns a Stream object.

Get Stream

GET /streams/{id} (Protected)

Returns a stream with all its child particles, enriched with seen/ack state.

Response: returns a Stream object.

Update Stream

PATCH /streams/{id} (Protected)

Updates a stream's name and/or description. Status is not affected (use the open/close endpoints instead). Only provided fields are updated.

Request Body:

{
  "name": "New Name",
  "description": "New description"
}
  • Both fields are optional — omit a field to leave it unchanged
  • name cannot be empty if provided

Response: returns the updated Stream object.

Create Stream Particle

POST /streams/{id}/particles (Protected)

Creates a child particle inside a stream. Child particles inherit visibility from the stream.

Request Body:

{
  "type": "text|media|file|quest|paper",
  "data": {}
}
  • Cannot create stream or folder types as children
  • For media and file types, data must include a valid object_id from depot

Response: 201 Created — returns a StreamParticle object.

Open Stream

POST /streams/{id}/open (Protected)

Opens a closed stream. Fails with 409 if capacity would be exceeded.

Close Stream

POST /streams/{id}/close (Protected)

Closes an open stream.

Add Members to Stream

POST /streams/{id}/members (Protected)

Request Body:

{
  "emails": ["user@example.com"]
}

Remove Members from Stream

DELETE /streams/{id}/members (Protected)

Request Body:

{
  "emails": ["user@example.com"]
}

Particles

List Particles

GET /networks/{network_id}/particles (Protected)

Query Parameters:

  • parent_id (optional): Filter by parent particle
  • cursor (optional): Pagination cursor
  • direction (optional): after or before (default: after)
  • type (optional, repeatable): Filter by particle type

Response enrichment: Each particle in the response includes:

  • seen (boolean): Whether the requester has marked this particle as seen
  • acks (array): List of acknowledgments [{email, acked_at}]
  • unseen_count (integer, streams only): Count of unseen child particles

Get Particle

GET /particles/{id} (Protected)

Update Particle

PATCH /particles/{id} (Protected)

Request Body:

{
  "data": {}
}

Delete Particle

DELETE /particles/{id} (Protected)

Deletes the particle and all children. If it references a depot object, that is also deleted.

Download Particle

GET /particles/{id}/download (Protected)

Returns a 302 redirect to a signed download URL. Only works for media and file particles.

Mark Seen

POST /particles/{id}/seen (Protected)

Marks a particle as seen by the requester. This is private state, only visible to the requester.

Returns 204 No Content on success.

Mark Seen (Batch)

POST /particles/seen (Protected)

Marks multiple particles as seen by the requester.

Request Body:

{
  "particle_ids": ["particle_uuid1", "particle_uuid2"]
}

Returns 204 No Content on success.

Acknowledge Particle

POST /particles/{id}/ack (Protected)

Acknowledges a particle. Acknowledgments are public and permanent, visible to all users with access. Also marks the particle as seen.

Returns 204 No Content on success.


Depot (File Storage)

Prepare Upload

POST /depot/upload (Protected)

Prepares a signed URL for direct upload to GCS.

Request Body:

{
  "network_id": "network_uuid",
  "name": "filename.png",
  "content_type": "image/png",
  "content_length": 12345
}

Response:

{
  "object_id": "uuid",
  "upload_url": "https://storage.googleapis.com/...",
  "upload_headers": { "Content-Type": "image/png" }
}

Confirm Upload

POST /depot/objects/{id}/confirm (Protected)

Confirms that an upload has been completed.

Response:

{
  "id": "uuid",
  "name": "filename.png",
  "content_type": "image/png",
  "content_length": 12345,
  "contains_content": true,
  "created_at": "..."
}

Upload Flow

  1. POST /depot/upload — get signed URL and object_id
  2. Upload file directly to GCS using the signed URL
  3. POST /depot/objects/{id}/confirm — mark upload complete
  4. Create a media or file particle with the object_id in its data

Object Reference

Human

Field Type Description
id string | null Unique identifier. Null if not yet registered.
email string Email address (stable identifier).
email_prefix string The local part of the email (before @).
created_at string | null ISO 8601 timestamp. Null if not registered.
{
  "id": "abc-123",
  "email": "alice@example.com",
  "email_prefix": "alice",
  "created_at": "2025-01-15T10:30:00Z"
}

Network

Field Type Description
id string Unique identifier.
name string Display name of the network.
admin_human Human The network administrator.
humans Human[] All members of the network (including admin).
open_stream_count integer Number of currently open streams.
open_stream_capacity integer Maximum number of concurrent open streams (default: 5).
created_at string ISO 8601 timestamp.
{
  "id": "net-456",
  "name": "My Team",
  "admin_human": { "id": "abc-123", "email": "alice@example.com", "email_prefix": "alice", "created_at": "..." },
  "humans": [
    { "id": "abc-123", "email": "alice@example.com", "email_prefix": "alice", "created_at": "..." }
  ],
  "open_stream_count": 2,
  "open_stream_capacity": 5,
  "created_at": "2025-01-15T10:30:00Z"
}

Stream

Field Type Description
id string Unique identifier (this is a particle ID).
name string Stream name.
description string Stream description.
status string "open", "closed", or "unspecified".
members string[] Emails of stream members. Omitted for network_all visibility.
particles StreamParticle[] Child particles in the stream.
unseen_count integer Number of unseen child particles for the requester.
{
  "id": "p-001",
  "name": "Sprint Planning",
  "description": "Weekly sync",
  "status": "open",
  "members": ["alice@example.com"],
  "particles": [],
  "unseen_count": 0
}

StreamParticle

Field Type Description
id string Unique identifier.
type string One of: media, file, text, quest, paper.
data object Type-specific payload (see Particle Data by Type).
created_by_email string Email of the creator.
seen boolean Whether the requester has seen this particle.
acks AckInfo[] Acknowledgments from users.
updated_at string ISO 8601 timestamp.
created_at string ISO 8601 timestamp.

Particle

Field Type Description
id string Unique identifier.
type string One of: stream, folder, media, file, text, quest, paper.
network_id string The network this particle belongs to.
parent_id string | null Parent particle ID, if nested.
created_by_email string Email of the creator.
visibility string "network_all", "custom", or "inherited".
stream_status string | null Only on stream type: "open" or "closed".
data object Type-specific payload (see Particle Data by Type).
download_url string | null Signed download URL. Only on media/file particles.
seen boolean | null Whether the requester has seen this particle. Only in list responses.
acks AckInfo[] Acknowledgments. Only in list responses.
unseen_count integer | null Unseen child count. Only on stream particles in list responses.
updated_at string ISO 8601 timestamp.
created_at string ISO 8601 timestamp.

AckInfo

Field Type Description
email string Email of the user who acknowledged.
acked_at string ISO 8601 timestamp of the acknowledgment.

ParticleList

Returned by GET /networks/{network_id}/particles.

Field Type Description
particles Particle[] Array of enriched particle objects.
has_more boolean Whether more results exist beyond this page.
next_cursor string | null Cursor to fetch the next page.
prev_cursor string | null Cursor to fetch the previous page.

DepotObject

Returned by POST /depot/objects/{id}/confirm.

Field Type Description
id string Unique object identifier.
name string Original filename.
content_type string MIME type (e.g. image/png).
content_length integer Size in bytes.
contains_content boolean Whether the object has been uploaded successfully.
created_at string ISO 8601 timestamp.

Particle Data by Type

The data field on a Particle is a JSON object whose schema depends on the particle's type.

stream

Field Type Description
name string Stream name (required).
status string "open" or "closed" (required).
description string | null Stream description (optional).
{ "name": "Sprint Planning", "status": "open", "description": "Weekly sync" }

folder

Field Type Description
name string Folder name (required).
color string | null Display color (optional).
{ "name": "Design Assets", "color": "#FF5733" }

media

Field Type Description
object_id string Reference to depot storage object (required).
mime_type string MIME type of the media (required).
duration_ms integer Duration in milliseconds, must be > 0 (required).
{ "object_id": "obj-123", "mime_type": "image/jpeg", "duration_ms": 5000 }

file

Field Type Description
object_id string Reference to depot storage object (required).
filename string Original filename (required).
mime_type string MIME type (required).
size integer File size in bytes, must be > 0 (required).
{ "object_id": "obj-456", "filename": "report.pdf", "mime_type": "application/pdf", "size": 204800 }

text

Field Type Description
content string Text content (required).
{ "content": "Hello world" }

quest

Field Type Description
title string Quest/task title (required).
description string Details about the quest (required).
status string | null Current status (optional, e.g. "todo", "in_progress", "done").
assigned_to string | null Email of the assigned user (optional).
due_date string | null ISO date string (optional, e.g. "2025-03-01").
{ "title": "Fix login bug", "description": "Login fails on Safari", "status": "todo", "assigned_to": "bob@example.com" }

paper

Field Type Description
title string Document title (required).
content string Document body in markdown (required).
{ "title": "Architecture RFC", "content": "## Overview\n..." }

Visibility

Particles support three visibility modes:

Mode Description
network_all Visible to all network members.
custom Visible only to specified members (requires members list).
inherited Inherits visibility from parent particle. Used for child particles in streams.

Root-level particles (streams, folders) use network_all or custom. Child particles created via POST /streams/{id}/particles automatically use inherited.


Error Responses

All endpoints return standard HTTP status codes with plain text error bodies:

  • 400 Bad Request — invalid input or missing required fields
  • 401 Unauthorized — missing or invalid auth token
  • 403 Forbidden — user lacks permission (not a member, not creator, not admin)
  • 404 Not Found — resource doesn't exist
  • 409 Conflict — state conflict (stream already open/closed, capacity exceeded)
  • 500 Internal Server Error