Developer API

StelaSpace lets your AI agents and scripts publish HTML documents (reports, dashboards, visualizations) and get back a permanent, shareable URL. The workflow is one call: generate HTML → POST it → share the link.

All endpoints are under https://stelaspace.com/api/v1 and return JSON. Documents live inside a Space, which belongs to a Team. An API key is scoped to the team it was created in.

Authentication

Authenticate every request with a Bearer token in the Authorization header. Create a key in Settings → API Keys; the full key (prefixed ss_sk_) is shown once at creation — store it securely. The key determines which team the request acts on.

Header
Authorization: Bearer ss_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Verify a key works with the ping endpoint:

GET/api/v1/ping
curl
curl https://stelaspace.com/api/v1/ping \
  -H "Authorization: Bearer ss_sk_your_key_here"
200 OK
{ "ok": true, "teamId": "uuid", "apiKeyId": "uuid" }

Connect a coding agent (MCP)

StelaSpace ships a local Model Context Protocol server (stelaspace-mcp) for coding agents that work with your local files — Claude Code and Codex. The agent generates a report to disk, then publishes it by path, so large dashboards upload reliably. Tools: publish_file, list_spaces, and list_documents.

It authenticates with your ss_sk_ API key via the STELASPACE_API_KEY environment variable.

Claude Code

terminal
claude mcp add stelaspace --scope user \
  --env STELASPACE_API_KEY=ss_sk_your_key \
  -- npx -y stelaspace-mcp

Codex

Add to ~/.codex/config.toml (or run codex mcp add):

~/.codex/config.toml
[mcp_servers.stelaspace]
command = "npx"
args = ["-y", "stelaspace-mcp"]
env = { STELASPACE_API_KEY = "ss_sk_your_key" }

Then just ask the agent to publish — e.g. “build the sales dashboard and publish the HTML to my reports space as public.”Reusing a document's slug publishes a new version automatically.

Note: Claude Desktop (chat) isn't supported for publishing — its sandbox can't share files with a local server, so it can't upload local reports.

Publish a document

POST/api/v1/documents

Send a multipart/form-data body. Publishing to a slug that already exists creates a new version of that document.

FieldTypeRequiredDescription
filefileYesAn .html or .htm file, max 5 MB.
spaceSlugstringYesSlug of an existing Space in the team your API key belongs to.
slugstringNoDocument slug. Defaults to a slug from the title. Reusing one adds a version.
titlestringNoDefaults to the HTML <title>, then the filename.
tagsstringNoComma-separated, e.g. finance,q2.
visibilitystringNopublic or private (default private).
curl
curl -X POST https://stelaspace.com/api/v1/documents \
  -H "Authorization: Bearer ss_sk_your_key_here" \
  -F "file=@report.html" \
  -F "spaceSlug=monthly-reports" \
  -F "title=June Revenue" \
  -F "tags=finance,q2" \
  -F "visibility=public"
Node.js (18+)
import { readFile } from "node:fs/promises";

const html = await readFile("report.html");

const form = new FormData();
form.set("file", new Blob([html], { type: "text/html" }), "report.html");
form.set("spaceSlug", "monthly-reports");
form.set("title", "June Revenue");
form.set("visibility", "public");

const res = await fetch("https://stelaspace.com/api/v1/documents", {
  method: "POST",
  headers: { Authorization: "Bearer " + process.env.STELASPACE_API_KEY },
  body: form,
});

const doc = await res.json();
console.log(doc.url);
201 Created
{
  "documentId": "uuid",
  "versionNumber": 1,
  "url": "https://stelaspace.com/t/acme/s/monthly-reports/d/june-revenue",
  "blobUrl": "https://....public.blob.vercel-storage.com/...",
  "slug": "june-revenue",
  "publishedAt": "2026-06-04T10:00:00.000Z"
}

List documents

GET/api/v1/documents

Query params: spaceSlug (required), page (default 1), limit (default 20, max 100), tag, and visibility.

curl
curl "https://stelaspace.com/api/v1/documents?spaceSlug=monthly-reports&limit=20" \
  -H "Authorization: Bearer ss_sk_your_key_here"
200 OK
{
  "data": [
    {
      "id": "uuid",
      "slug": "june-revenue",
      "title": "June Revenue",
      "description": null,
      "tags": ["finance", "q2"],
      "visibility": "public",
      "viewCount": 12,
      "createdAt": "2026-06-04T10:00:00.000Z",
      "updatedAt": "2026-06-04T10:00:00.000Z"
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 1
}

Get a document

GET/api/v1/documents/{slug}

Requires the spaceSlug query param. Returns the document plus its versions (newest first).

curl
curl "https://stelaspace.com/api/v1/documents/june-revenue?spaceSlug=monthly-reports" \
  -H "Authorization: Bearer ss_sk_your_key_here"
200 OK
{
  "id": "uuid",
  "slug": "june-revenue",
  "title": "June Revenue",
  "tags": ["finance", "q2"],
  "visibility": "public",
  "versions": [
    { "versionNumber": 2, "blobUrl": "https://...", "fileSizeBytes": 20481, "createdAt": "..." },
    { "versionNumber": 1, "blobUrl": "https://...", "fileSizeBytes": 18233, "createdAt": "..." }
  ]
}

Delete a document

DELETE/api/v1/documents/{slug}

Requires the spaceSlug query param. Deletes the document, all of its versions, and the underlying stored files.

curl
curl -X DELETE "https://stelaspace.com/api/v1/documents/june-revenue?spaceSlug=monthly-reports" \
  -H "Authorization: Bearer ss_sk_your_key_here"
200 OK
{ "deleted": true, "slug": "june-revenue" }

Create a space

POST/api/v1/spaces

Send a JSON body. The new Space is created in the team your API key belongs to. Use this to set up a Space before publishing documents into it.

FieldTypeRequiredDescription
namestringYesDisplay name (1–100 chars).
slugstringNoURL slug. Defaults to a slug from the name. Must be unique in the team.
descriptionstringNoOptional, up to 500 chars.
defaultVisibilitystringNopublic or private (default private).
curl
curl -X POST https://stelaspace.com/api/v1/spaces \
  -H "Authorization: Bearer ss_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name":"Monthly Reports","slug":"monthly-reports","defaultVisibility":"private"}'
201 Created
{
  "id": "uuid",
  "name": "Monthly Reports",
  "slug": "monthly-reports",
  "description": null,
  "defaultVisibility": "private",
  "url": "https://stelaspace.com/t/acme/s/monthly-reports",
  "createdAt": "2026-06-04T10:00:00.000Z"
}

Returns 409 if a Space with that slug already exists in the team.

List spaces

GET/api/v1/spaces

Lists the Spaces in the team your API key belongs to — useful for discovering valid spaceSlug values.

curl
curl https://stelaspace.com/api/v1/spaces \
  -H "Authorization: Bearer ss_sk_your_key_here"
200 OK
{
  "data": [
    {
      "id": "uuid",
      "name": "Monthly Reports",
      "slug": "monthly-reports",
      "description": null,
      "defaultVisibility": "private",
      "createdAt": "..."
    }
  ]
}

Errors

Errors return a JSON body { "error": "message" } with one of these statuses:

StatusMeaning
200 / 201Success.
400Bad request — missing field, wrong file type, or file over 5 MB.
401Missing or invalid API key.
404Space or document not found in the team your API key belongs to.
409Conflict — a Space with that slug already exists.