API Documentation
Publish nails in 3 API calls. No account required for temporary nails.
Quick Start
Publish an HTML file to a live URL in under 10 seconds:
# 1. Create a nail curl -X POST https://api.sharehammer.com/api/v1/nails \ -H "Content-Type: application/json" \ -d '{"files": [{"path": "index.html", "size": 1024, "contentType": "text/html"}]}' # Response includes presigned upload URLs # { "slug": "abc123", "url": "https://abc123.sharehammer.com", "upload": { "files": [...] } } # 2. Upload your file to the presigned URL curl -X PUT "UPLOAD_URL_FROM_STEP_1" \ -H "Content-Type: text/html" \ --data-binary @index.html # 3. Finalize — your nail is live! curl -X POST https://api.sharehammer.com/api/v1/nails/SLUG/finalize # { "slug": "abc123", "url": "https://abc123.sharehammer.com", "status": "active" }
What are Nails and Forges?
Nails
A nail is the basic unit of content in ShareHammer. It can be a single file, a folder of files, or a complete static site. Every nail gets a unique URL at {slug}.sharehammer.com.
Forges
A forge is a collection of nails — use them to organize by project, client, or team. Forges are optional; nails work perfectly fine without one.
Authentication
Anonymous requests create temporary nails that expire in 24 hours. For permanent nails, authenticate with an API key.
Get an API key
# 1. Request a code curl -X POST https://api.sharehammer.com/api/v1/auth/send-code \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' # 2. Verify the code from your email curl -X POST https://api.sharehammer.com/api/v1/auth/verify \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com", "code": "123456", "grant": "api_key"}' # { "api_key": "sh_live_...", "user": { "id": 1, "email": "you@example.com" } }
Use the API key in all subsequent requests:
curl -H "Authorization: Bearer sh_live_..." ...
Create Nail
Request body
| Field | Type | Description |
|---|---|---|
files | array | List of files to publish |
files[].path | string | Relative path, e.g. index.html |
files[].size | integer | File size in bytes |
files[].contentType | string | MIME type, e.g. text/html |
Example
curl -X POST https://api.sharehammer.com/api/v1/nails \ -H "Content-Type: application/json" \ -d '{"files": [{"path": "index.html", "size": 1024, "contentType": "text/html"}]}'
import requests resp = requests.post("https://api.sharehammer.com/api/v1/nails", json={ "files": [{ "path": "index.html", "size": 1024, "contentType": "text/html" }] }) data = resp.json() print(data["url"]) # https://abc123.sharehammer.com
const resp = await fetch("https://api.sharehammer.com/api/v1/nails", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ files: [{ path: "index.html", size: 1024, contentType: "text/html" }] }) }); const data = await resp.json(); console.log(data.url); // https://abc123.sharehammer.com
Response
{
"slug": "abc123",
"url": "https://abc123.sharehammer.com",
"upload": {
"files": [
{ "path": "index.html", "uploadUrl": "https://...", "contentType": "text/html" }
]
}
}
Upload Files
Set the Content-Type header to match the contentType from the create response. Send raw file bytes as the body.
Example
curl -X PUT "UPLOAD_URL_FROM_STEP_1" \ -H "Content-Type: text/html" \ --data-binary @index.html
import requests with open("index.html", "rb") as f: requests.put( upload_url, data=f.read(), headers={"Content-Type": "text/html"} )
const file = await Deno.readFile("index.html"); // or: const file = fs.readFileSync("index.html"); await fetch(uploadUrl, { method: "PUT", headers: { "Content-Type": "text/html" }, body: file });
Finalize
Example
curl -X POST https://api.sharehammer.com/api/v1/nails/SLUG/finalize
import requests resp = requests.post(f"https://api.sharehammer.com/api/v1/nails/{slug}/finalize") print(resp.json()) # {"slug": "abc123", "url": "https://abc123.sharehammer.com", "status": "active"}
const resp = await fetch( `https://api.sharehammer.com/api/v1/nails/${slug}/finalize`, { method: "POST" } ); const data = await resp.json(); console.log(data.status); // "active"
Response
{
"slug": "abc123",
"url": "https://abc123.sharehammer.com",
"status": "active"
}
Update Nail
Same request body as create. After uploading changed files, call POST /nails/{slug}/finalize.
Update Metadata
| Field | Type | Description |
|---|---|---|
title | string | Nail title |
description | string | Nail description |
og_image_url | string | Open Graph image URL |
List Nails
Query parameters
| Param | Type | Description |
|---|---|---|
limit | integer | Number of nails to return. Default 20, max 100. |
cursor | string | Base64-encoded created_at timestamp for fetching the next page. Use the next_cursor value from a previous response. |
Example response
{
"nails": [
{
"slug": "abc123",
"url": "https://abc123.sharehammer.com",
"status": "active",
"title": "My Nail",
"created_at": "2025-01-15T10:30:00Z",
"file_count": 3,
"total_size": 24576
}
],
"pagination": {
"next_cursor": "MjAyNS0wMS0xNVQxMDozMDowMFo=",
"has_more": true
}
}
Get Nail
Example response
{
"slug": "abc123",
"url": "https://abc123.sharehammer.com",
"status": "active",
"title": "My Nail",
"description": "A demo nail",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:31:00Z",
"files": [
{ "path": "index.html", "size": 1024, "contentType": "text/html" },
{ "path": "style.css", "size": 2048, "contentType": "text/css" }
],
"total_size": 3072
}
Delete Nail
Example response
{
"deleted": true,
"slug": "abc123"
}
List Forges
Example
curl https://api.sharehammer.com/api/v1/forges \ -H "Authorization: Bearer sh_live_..."
Response
{
"forges": [
{
"id": 1,
"name": "My Project",
"slug": "my-project",
"color": "blue",
"nail_count": 5,
"created_at": "2025-01-15T10:30:00Z"
}
]
}
Create Forge
Request body
| Field | Type | Description |
|---|---|---|
name | string | Forge name |
color | string | One of: slate, red, amber, green, blue, purple, pink, teal |
Example
curl -X POST https://api.sharehammer.com/api/v1/forges \ -H "Authorization: Bearer sh_live_..." \ -H "Content-Type: application/json" \ -d '{"name": "My Project", "color": "blue"}'
Response
{
"forge": {
"id": 1,
"name": "My Project",
"slug": "my-project",
"color": "blue"
}
}
Delete Forge
Example
curl -X DELETE https://api.sharehammer.com/api/v1/forges/my-project \ -H "Authorization: Bearer sh_live_..."
Response
{
"deleted": true,
"slug": "my-project"
}
Assign Nail to Forge
Request body
| Field | Type | Description |
|---|---|---|
forge_id | integer | null | Forge ID to assign, or null to remove from forge |
Example
curl -X PATCH https://api.sharehammer.com/api/v1/nails/abc123 \ -H "Authorization: Bearer sh_live_..." \ -H "Content-Type: application/json" \ -d '{"forge_id": 1}' # To remove from forge: # -d '{"forge_id": null}'
Filter Nails by Forge
Example
curl https://api.sharehammer.com/api/v1/nails?forge=my-project \ -H "Authorization: Bearer sh_live_..."
Returns the same response format as List Nails, filtered to only include nails in the specified forge.
Custom Domains
proxy.sharehammer.com.{ "hostname": "example.com" }
Domain Links
{ "siteSlug": "abc123", "path": "" }
Claiming Nails
When you create a nail without authentication, the response includes a claim_token and a claim_url. These let you (or anyone you share them with) claim the nail later — assigning it to an account and making it permanent.
How it works
- Create a nail anonymously — the response includes
claim_tokenandclaim_url - The
claim_urlis a link that can be shared — visiting it while signed in will claim the nail - You can also claim programmatically via the API (see below)
- Claiming a nail assigns it to your account and makes it permanent (removes the 24-hour expiry)
- A nail can only be claimed once
Claim via link
Share or visit the claim_url from the create response. If you're signed in to ShareHammer, the nail will be claimed to your account automatically.
Claim via API
Request body
| Field | Type | Description |
|---|---|---|
claim_token | string | The claim token from the anonymous create response |
Example
curl -X POST https://api.sharehammer.com/api/v1/nails/claim \ -H "Authorization: Bearer sh_live_..." \ -H "Content-Type: application/json" \ -d '{"claim_token": "ct_abc123..."}' # Response # { "slug": "abc123", "url": "https://abc123.sharehammer.com", "status": "active", "claimed": true }
Project Binding (.sharehammer.json)
When working with the CLI or AI agents, you can bind a local directory to a specific nail by creating a .sharehammer.json file:
{
"slug": "my-project-docs",
"forge": "client-work"
}
How it works
- The CLI reads this file on deploy and updates the existing nail instead of creating a new one
- After the first deploy, the CLI writes this file automatically
- AI agents (via MCP) should check for this file before deploying
Create manually
sharehammer init my-project-docs
Or let the CLI create it
sharehammer deploy ./docs --slug my-project-docs # Creates .sharehammer.json automatically
Priority order for slug resolution
--slugflag (highest).sharehammer.jsonslug field- Random generated slug (lowest)
Fields
| Field | Required | Description |
|---|---|---|
slug | Yes | The nail slug to deploy to |
forge | No | Optional forge slug for organization |
Supported Formats
ShareHammer auto-detects file types and renders them with the best viewer. No configuration needed.
| Format | Extensions | What happens |
|---|---|---|
| HTML | .html .htm | Served as-is with OG metadata injection |
| Markdown | .md | Rendered with syntax highlighting, table of contents, lightbox images |
| CSV / TSV | .csv .tsv | Interactive data dashboard with charts, sorting, and stats |
| Code | .js .ts .py .go .rs .java .swift .c .cpp .rb .php .sh .sql .yaml .toml | Syntax highlighted viewer |
| JSON | .json | Formatted, collapsible viewer |
| Mermaid | .mmd .mermaid | Rendered diagrams with pan/zoom |
| Excalidraw | .excalidraw | Live whiteboard rendering |
| Images | .png .jpg .gif .svg .webp .avif | Displayed with lightbox |
| Video | .mp4 .webm | HTML5 video player |
| Audio | .mp3 | HTML5 audio player |
.pdf | Embedded PDF viewer |
For multi-file uploads: if no index.html is present, an auto-generated directory listing is shown with viewer links for each file.
Limits
| Anonymous | Authenticated | |
|---|---|---|
| Rate limit | 5 req/hour | 60 req/hour |
| Max file size | 10 MB | 50 MB |
| Max total size | 10 MB | 100 MB |
| Max files | 50 | 200 |
| Expiry | 24 hours | Permanent |
| Custom domains | — | 3 |
Errors
All errors return JSON with error and message fields:
{ "error": "invalid_body", "message": "At least one file required", "status": 400 }
Status codes
| Status | Meaning |
|---|---|
| 400 | Bad request — check request body |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — accessing a nail you don't own |
| 404 | Not found — nail or resource doesn't exist |
| 409 | Conflict — slug already taken, domain already registered, or nail already claimed |
| 413 | Payload Too Large — file exceeds the size limit |
| 429 | Rate limited — wait and retry |
Rate limit headers
Rate-limited responses (429) include these headers to help you handle retries:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
Retry-After | Seconds to wait before retrying |