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:
- Sign in with GitHub to create or access your dashboard account.
- Open the dashboard after sign-in.
- Generate your API key from the dashboard. It is displayed once, so copy it and store it securely.
- 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_keyAPI 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_abc123X-Artifact-Title sets a display name for the artifact shown in your dashboard. Supported on POST and PUT.
Endpoints
/v1/artifactsCreate 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.
/v1/artifacts/:idUpdate 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
}/v1/artifactsList 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.
/v1/artifacts/:idDelete 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
| Field | Type | Description |
|---|---|---|
| type | string | HTML tag name or catalog component name |
| props | object? | HTML attributes or component props. style accepts objects (camelCase → kebab-case) |
| children | string[]? | IDs of child elements. Takes precedence over text |
| text | string? | 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 Code | Meaning | When It Happens |
|---|---|---|
401 | Unauthorized | Missing, invalid, or malformed Authorization header. |
402 | Payment Required | Free tier artifact limit reached. Upgrade to Pro for more artifacts. |
413 | Payload Too Large | HTML body exceeds the maximum size limit (5 MB). |
415 | Unsupported Media Type | Content-Type is not text/html or application/json. |
429 | Too Many Requests | Per-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.