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.
Authorization: Bearer ss_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxVerify a key works with the ping endpoint:
/api/v1/pingcurl https://stelaspace.com/api/v1/ping \
-H "Authorization: Bearer ss_sk_your_key_here"{ "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
claude mcp add stelaspace --scope user \
--env STELASPACE_API_KEY=ss_sk_your_key \
-- npx -y stelaspace-mcpCodex
Add to ~/.codex/config.toml (or run codex mcp add):
[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
/api/v1/documentsSend a multipart/form-data body. Publishing to a slug that already exists creates a new version of that document.
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | An .html or .htm file, max 5 MB. |
spaceSlug | string | Yes | Slug of an existing Space in the team your API key belongs to. |
slug | string | No | Document slug. Defaults to a slug from the title. Reusing one adds a version. |
title | string | No | Defaults to the HTML <title>, then the filename. |
tags | string | No | Comma-separated, e.g. finance,q2. |
visibility | string | No | public or private (default private). |
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"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);{
"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
/api/v1/documentsQuery params: spaceSlug (required), page (default 1), limit (default 20, max 100), tag, and visibility.
curl "https://stelaspace.com/api/v1/documents?spaceSlug=monthly-reports&limit=20" \
-H "Authorization: Bearer ss_sk_your_key_here"{
"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
/api/v1/documents/{slug}Requires the spaceSlug query param. Returns the document plus its versions (newest first).
curl "https://stelaspace.com/api/v1/documents/june-revenue?spaceSlug=monthly-reports" \
-H "Authorization: Bearer ss_sk_your_key_here"{
"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
/api/v1/documents/{slug}Requires the spaceSlug query param. Deletes the document, all of its versions, and the underlying stored files.
curl -X DELETE "https://stelaspace.com/api/v1/documents/june-revenue?spaceSlug=monthly-reports" \
-H "Authorization: Bearer ss_sk_your_key_here"{ "deleted": true, "slug": "june-revenue" }Create a space
/api/v1/spacesSend 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.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name (1–100 chars). |
slug | string | No | URL slug. Defaults to a slug from the name. Must be unique in the team. |
description | string | No | Optional, up to 500 chars. |
defaultVisibility | string | No | public or private (default private). |
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"}'{
"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
/api/v1/spacesLists the Spaces in the team your API key belongs to — useful for discovering valid spaceSlug values.
curl https://stelaspace.com/api/v1/spaces \
-H "Authorization: Bearer ss_sk_your_key_here"{
"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:
| Status | Meaning |
|---|---|
200 / 201 | Success. |
400 | Bad request — missing field, wrong file type, or file over 5 MB. |
401 | Missing or invalid API key. |
404 | Space or document not found in the team your API key belongs to. |
409 | Conflict — a Space with that slug already exists. |