API Documentation

Everything you need to create, update, list, and delete hosted HTML artifacts via the my-site.io REST API.

Authentication

All API requests require a Bearer token in the Authorization header. Here's how to get your API key:

  1. Sign in with GitHub to create or access your dashboard account.
  2. Open the dashboard after sign-in.
  3. Generate your API key from the dashboard. It is displayed once, so copy it and store it securely.
  4. If you lose your key, regenerate it from the dashboard. Regenerating invalidates the old key.

Include your key in every request:

Authorization: Bearer your_artifact_api_key

API keys are prefixed with artifact_ and should be kept secret. Never commit them to source control.


Optional Client Headers

Agent and SDK callers can send lightweight client metadata for better analytics and debugging. These headers are optional and do not affect authentication or response shape.

X-Artifact-Title: My Dashboard
X-Client-Name: codex-cli
X-Client-Version: 1.2.3
X-Run-Id: run_abc123

X-Artifact-Title sets a display name for the artifact shown in your dashboard. Supported on POST and PUT.


Endpoints

POST/v1/artifacts

Create Artifact

Upload raw HTML and receive a live public URL. The HTML is stored and immediately accessible.

Example Request

curl -X POST https://adjoining-bison-868.convex.site
/v1/artifacts \
  -H "Authorization: Bearer your_artifact_api_key" \
  -H "Content-Type: text/html" \
  -H "X-Artifact-Title: My Page" \
  -d '<html><body><h1>Hello World</h1></body></html>'

Example Response

{
  "id": "abc123",
  "url": "https://adjoining-bison-868.convex.site
/a/abc123"
}

Free tier: max 3 active artifacts with 24-hour expiry. Pro tier: higher limits, no expiry. The X-Artifact-Title header is optional and sets a display name visible in the dashboard.

PUT/v1/artifacts/:id

Update Artifact

Replace the HTML content of an existing artifact you own. The public URL stays the same.

Example Request

curl -X PUT https://adjoining-bison-868.convex.site
/v1/artifacts/abc123 \
  -H "Authorization: Bearer your_artifact_api_key" \
  -H "Content-Type: text/html" \
  -H "X-Artifact-Title: Updated Title" \
  -d '<html><body><h1>Updated content</h1></body></html>'

Example Response

{
  "id": "abc123",
  "url": "https://adjoining-bison-868.convex.site
/a/abc123",
  "updated": true
}
GET/v1/artifacts

List Artifacts

Retrieve a list of all your active artifacts with their metadata.

Example Request

curl https://adjoining-bison-868.convex.site
/v1/artifacts \
  -H "Authorization: Bearer your_artifact_api_key"

Example Response

[
  {
    "id": "abc123",
    "title": "My Page",
    "url": "https://adjoining-bison-868.convex.site
/a/abc123",
    "createdAt": 1710900000000,
    "updatedAt": 1710900000000
  }
]

Only returns your own artifacts. Soft-deleted artifacts are excluded.

DELETE/v1/artifacts/:id

Delete Artifact

Soft-delete an artifact you own. The public URL will return 404 after deletion.

Example Request

curl -X DELETE https://adjoining-bison-868.convex.site
/v1/artifacts/abc123 \
  -H "Authorization: Bearer your_artifact_api_key"

Example Response

{
  "id": "abc123",
  "deleted": true
}

Deleting an artifact frees up your quota slot on the free tier.


JSON Render — Structured Input

Instead of raw HTML, you can send a JSON spec with Content-Type: application/json. The spec is validated and rendered to a full HTML page server-side. This is ideal for AI agents and programmatic content generation.

Spec Format

A flat tree of elements with ID-based references:

{
  "root": "page",
  "meta": { "title": "My Page", "style": "body { margin: 0; }" },
  "elements": {
    "page": { "type": "div", "children": ["heading", "content"] },
    "heading": { "type": "h1", "text": "Hello World" },
    "content": { "type": "p", "text": "Built from JSON." }
  }
}

Element Fields

FieldTypeDescription
typestringHTML tag name or catalog component name
propsobject?HTML attributes or component props. style accepts objects (camelCase → kebab-case)
childrenstring[]?IDs of child elements. Takes precedence over text
textstring?Text content (HTML-escaped automatically)

Example: Create with JSON

curl -X POST https://adjoining-bison-868.convex.site
/v1/artifacts \
  -H "Authorization: Bearer your_artifact_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "root": "r",
    "meta": { "title": "Hello" },
    "elements": {
      "r": { "type": "h1", "text": "Hello from JSON!" }
    }
  }'

Security: script, iframe, object tags are blocked. Event handler attributes (onclick, etc.) and javascript: URLs are rejected. Max 500 elements, 50 levels of nesting.


Component Catalog

High-level components that expand into pre-styled HTML. Use them as the type field in any element — no CSS knowledge required. Can be mixed with raw HTML elements in the same spec.

metric-card

Single metric display with label, value, and optional trend.

{ "type": "metric-card", "props": { "label": "Revenue", "value": "$4.2M", "trend": "+12%" } }

stat-grid

Grid layout for metrics. Accepts children (typically metric-cards). Optional columns prop (default: 3).

{ "type": "stat-grid", "props": { "columns": 3 }, "children": ["m1", "m2", "m3"] }

hero

Hero section with title, subtitle, and optional CTA button.

{ "type": "hero", "props": { "title": "Welcome", "subtitle": "Build something great", "ctaText": "Get Started", "ctaHref": "/start" } }

card

Generic container with optional title/subtitle. Accepts children for content.

{ "type": "card", "props": { "title": "Settings", "subtitle": "Manage your account" }, "children": ["content"] }

data-table

Renders a styled table from structured data. Pass column headers and row arrays.

{ "type": "data-table", "props": {
  "columns": ["Name", "Role", "Status"],
  "rows": [["Alice", "Engineer", "Active"], ["Bob", "Designer", "Away"]]
} }

section-header

Section title with optional subtitle.

{ "type": "section-header", "props": { "title": "Features", "subtitle": "Everything included" } }

badge

Pill-shaped label. Variants: default, success, warning, error.

{ "type": "badge", "props": { "text": "Live", "variant": "success" } }

blockquote-card

Styled quote with optional author and source attribution.

{ "type": "blockquote-card", "props": { "quote": "Ship it.", "author": "Every PM", "source": "Every standup" } }

cta-button

Call-to-action link styled as a button. Variants: primary (filled), secondary (outline).

{ "type": "cta-button", "props": { "text": "Sign Up", "href": "/signup", "variant": "primary" } }

nav-bar

Navigation bar with brand name and link items.

{ "type": "nav-bar", "props": {
  "brand": "Acme",
  "links": [{ "label": "Home", "href": "/" }, { "label": "Docs", "href": "/docs" }]
} }

footer

Page footer with text and optional links.

{ "type": "footer", "props": {
  "text": "© 2026 Acme Inc",
  "links": [{ "label": "Privacy", "href": "/privacy" }]
} }

Full Example — Dashboard with Catalog

curl -X POST https://adjoining-bison-868.convex.site
/v1/artifacts \
  -H "Authorization: Bearer your_artifact_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "root": "page",
    "elements": {
      "page": { "type": "div", "children": ["nav", "hero", "grid", "table", "foot"] },
      "nav": { "type": "nav-bar", "props": { "brand": "Acme", "links": [{"label": "Home", "href": "/"}] } },
      "hero": { "type": "hero", "props": { "title": "Dashboard", "subtitle": "Real-time overview" } },
      "grid": { "type": "stat-grid", "children": ["m1", "m2", "m3"] },
      "m1": { "type": "metric-card", "props": { "label": "Users", "value": "1,234", "trend": "+12%" } },
      "m2": { "type": "metric-card", "props": { "label": "Revenue", "value": "$42K", "trend": "+8%" } },
      "m3": { "type": "metric-card", "props": { "label": "Uptime", "value": "99.9%" } },
      "table": { "type": "data-table", "props": { "columns": ["Service", "Status"], "rows": [["API", "Healthy"], ["Auth", "Healthy"]] } },
      "foot": { "type": "footer", "props": { "text": "© 2026 Acme" } }
    }
  }'

Error Responses

All errors return JSON with an error field describing what went wrong.

Status CodeMeaningWhen It Happens
401UnauthorizedMissing, invalid, or malformed Authorization header.
402Payment RequiredFree tier artifact limit reached. Upgrade to Pro for more artifacts.
413Payload Too LargeHTML body exceeds the maximum size limit (5 MB).
415Unsupported Media TypeContent-Type is not text/html or application/json.
429Too Many RequestsPer-API-key rate limit exceeded. Check the Retry-After header for when to retry.

Example Error Response

{
  "error": "Unauthorized"
}

By using the my-site.io API, you agree to our Acceptable Use Policy. Content that violates the policy may be removed without notice.