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" }

Next Steps

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

POST /api/v1/nails Public
Create a new nail and get presigned upload URLs for each file.

Request body

FieldTypeDescription
filesarrayList of files to publish
files[].pathstringRelative path, e.g. index.html
files[].sizeintegerFile size in bytes
files[].contentTypestringMIME 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

PUT {uploadUrl} Public
Upload each file to its presigned R2 URL from the create response.

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

POST /api/v1/nails/{slug}/finalize Public
Finalize the nail after all files are uploaded. Makes it live.

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

PUT /api/v1/nails/{slug} Auth
Redeploy a nail with new files. Unchanged files (by SHA-256 hash) are skipped.

Same request body as create. After uploading changed files, call POST /nails/{slug}/finalize.

Update Metadata

PATCH /api/v1/nails/{slug} Auth
Update nail title, description, or OG image URL.
FieldTypeDescription
titlestringNail title
descriptionstringNail description
og_image_urlstringOpen Graph image URL

List Nails

GET /api/v1/nails Auth
List your nails with pagination.

Query parameters

ParamTypeDescription
limitintegerNumber of nails to return. Default 20, max 100.
cursorstringBase64-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

GET /api/v1/nails/{slug} Auth
Get nail details including file list.

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

DELETE /api/v1/nails/{slug} Auth
Permanently delete a nail and its files.

Example response

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

List Forges

GET /api/v1/forges Auth
List all forges in your account.

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

POST /api/v1/forges Auth
Create a new forge to organize your nails.

Request body

FieldTypeDescription
namestringForge name
colorstringOne 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

DELETE /api/v1/forges/{slug} Auth
Delete a forge. Nails in this forge are not deleted — they become unassigned.

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

PATCH /api/v1/nails/{slug} Auth
Assign a nail to a forge, or remove it from one.

Request body

FieldTypeDescription
forge_idinteger | nullForge 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

GET /api/v1/nails?forge={slug} Auth
Filter your nails list by forge slug.

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

POST /api/v1/domains Auth
Add a custom domain. CNAME it to proxy.sharehammer.com.
{ "hostname": "example.com" }
POST /api/v1/domains/{id}/links Auth
Link a nail to a domain path.
{ "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

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

POST /api/v1/nails/claim Auth
Claim an anonymous nail using its claim token. Requires authentication.

Request body

FieldTypeDescription
claim_tokenstringThe 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

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

  1. --slug flag (highest)
  2. .sharehammer.json slug field
  3. Random generated slug (lowest)

Fields

FieldRequiredDescription
slugYesThe nail slug to deploy to
forgeNoOptional forge slug for organization

Supported Formats

ShareHammer auto-detects file types and renders them with the best viewer. No configuration needed.

FormatExtensionsWhat happens
HTML.html .htmServed as-is with OG metadata injection
Markdown.mdRendered with syntax highlighting, table of contents, lightbox images
CSV / TSV.csv .tsvInteractive data dashboard with charts, sorting, and stats
Code.js .ts .py .go .rs .java .swift .c .cpp .rb .php .sh .sql .yaml .tomlSyntax highlighted viewer
JSON.jsonFormatted, collapsible viewer
Mermaid.mmd .mermaidRendered diagrams with pan/zoom
Excalidraw.excalidrawLive whiteboard rendering
Images.png .jpg .gif .svg .webp .avifDisplayed with lightbox
Video.mp4 .webmHTML5 video player
Audio.mp3HTML5 audio player
PDF.pdfEmbedded 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

AnonymousAuthenticated
Rate limit5 req/hour60 req/hour
Max file size10 MB50 MB
Max total size10 MB100 MB
Max files50200
Expiry24 hoursPermanent
Custom domains3

Errors

All errors return JSON with error and message fields:

{ "error": "invalid_body", "message": "At least one file required", "status": 400 }

Status codes

StatusMeaning
400Bad request — check request body
401Unauthorized — missing or invalid API key
403Forbidden — accessing a nail you don't own
404Not found — nail or resource doesn't exist
409Conflict — slug already taken, domain already registered, or nail already claimed
413Payload Too Large — file exceeds the size limit
429Rate limited — wait and retry

Rate limit headers

Rate-limited responses (429) include these headers to help you handle retries:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
Retry-AfterSeconds to wait before retrying

Getting Started

Quick Start Nails & Forges Authentication

Publishing

Create Nail Upload Files Finalize Update Nail Update Metadata

Management

List Nails Get Nail Delete Nail

Forges

List Forges Create Forge Delete Forge Assign Nail to Forge Filter by Forge

Domains

Custom Domains Domain Links

Claiming

Claiming Nails

Reference

Project Binding Supported Formats Limits Errors