ShipSite API
Instant static site hosting. Publish files and get a live URL at <slug>.shipsite.co.
Inspired by and API-compatible with here.now — whose elegant design made instant static hosting accessible to agents everywhere. ShipSite builds on that foundation with additional features: version history, instant rollback, ZIP export, and built-in analytics.
- Quick Start
- Authentication
- Create a Site
- Upload Files
- Finalize
- Update a Site
- Get Site Details
- List Sites
- Update Metadata
- Claim Anonymous Site
- Duplicate a Site
- Delete a Site
- Version History ShipSite
- Rollback ShipSite
- Download as ZIP ShipSite
- Analytics ShipSite
- Share via Email ShipSite
- Limits
Quick Start
No account needed. Create an anonymous site in three steps:
1. Create
curl -sS https://shipsite.co/api/v1/publish \
-H "content-type: application/json" \
-d '{"files":[{"path":"index.html","size":42,"contentType":"text/html"}]}'
Returns presigned upload URLs and a finalizeUrl.
2. Upload
# PUT each file to its presigned URL
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: text/html" \
--data-binary "<h1>Hello world</h1>"
3. Finalize
curl -sS -X POST "$FINALIZE_URL" \
-H "content-type: application/json" \
-d '{"versionId":"$VERSION_ID","claimToken":"$CLAIM_TOKEN"}'
Your site is now live at https://<slug>.shipsite.co/
Authentication
Passwordless via email OTP. Two steps to get an API key:
Request a code
POST/api/auth/agent/request-code
{"email": "you@example.com"}
Verify and get your key
POST/api/auth/agent/verify-code
{"email": "you@example.com", "code": "ABCD-EFGH"}
Returns: {"apiKey": "sk_..."}
Use it in requests:
Authorization: Bearer sk_your_api_key_here
Create a Site
POST/api/v1/publish
Request body:
{
"files": [
{"path": "index.html", "size": 1024, "contentType": "text/html"},
{"path": "style.css", "size": 512, "contentType": "text/css", "hash": "sha256:..."}
],
"viewer": {
"title": "My Project",
"description": "A demo site",
"ogImagePath": "og.png"
}
}
Response:
{
"slug": "bright-canvas-a7k2",
"siteUrl": "https://bright-canvas-a7k2.shipsite.co/",
"upload": {
"versionId": "01ABC...",
"uploads": [
{"path": "index.html", "method": "PUT", "url": "https://...", "headers": {"Content-Type": "text/html"}}
],
"skipped": ["style.css"],
"finalizeUrl": "https://shipsite.co/api/v1/publish/bright-canvas-a7k2/finalize",
"expiresInSeconds": 3600
},
"claimToken": "abc123...",
"claimUrl": "https://shipsite.co/claim?slug=bright-canvas-a7k2&token=abc123...",
"expiresAt": "2026-03-30T12:00:00.000Z",
"anonymous": true,
"warning": "IMPORTANT: Save the claimToken and claimUrl. They are returned only once."
}
claimToken, claimUrl, expiresAt, anonymous, and warning fields only appear for anonymous (unauthenticated) publishes. Save the claimToken — you need it to finalize, update, or claim the site.hash (SHA-256) for each file to enable incremental deploys. Files with matching hashes are skipped and copied server-side.Viewer field limits: title max 200 chars, description max 500 chars, ogImagePath max 500 chars.
Upload Files
PUT each file to its presigned URL from the create/update response. Uploads go directly to S3 — the server never proxies file bytes.
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: text/html" \
--data-binary @index.html
Presigned URLs expire after 1 hour. Use the uploads/refresh endpoint to get new ones.
Refresh upload URLs
POST/api/v1/publish/:slug/uploads/refresh
Requires auth. Returns fresh presigned URLs for the pending version.
Finalize
POST/api/v1/publish/:slug/finalize
{"versionId": "01ABC...", "claimToken": "abc123..."}
Activates the uploaded version. The site is now live. For authenticated sites, use your API key. For anonymous sites, include the claimToken from the create response.
Markdown files (.md, .markdown) are automatically converted to styled HTML during finalization.
Response:
{
"success": true,
"slug": "bright-canvas-a7k2",
"siteUrl": "https://bright-canvas-a7k2.shipsite.co/",
"previousVersionId": null,
"currentVersionId": "01ABC..."
}
Update a Site
PUT/api/v1/publish/:slug
{"files": [...], "claimToken": "..." }
Same format as create. For authenticated sites, use your API key. For anonymous sites, include the claimToken.
Get Site Details
GET/api/v1/publish/:slug
Authorization: Bearer sk_...
Requires auth. Returns site metadata, current/pending version IDs, and the file manifest.
List Sites
GET/api/v1/publishes
Returns all sites for the authenticated user.
Update Metadata
PATCH/api/v1/publish/:slug/metadata
{
"viewer": {"title": "New Title", "description": "Updated desc"},
"ttlSeconds": 86400,
"password": "secret"
}
Set ttlSeconds to auto-expire the site. Set to null to remove expiry.
Password protection
Set password to a non-empty string to lock the site behind a passcode. Visitors see a password prompt and must enter the correct code before any content is served. The browser remembers the password for 24 hours via a cookie.
Set password to null to remove protection:
// Lock
{"password": "my-secret-code"}
// Unlock
{"password": null}
Passwords are hashed with scrypt before storage and never stored in plain text.
API clients and agents can bypass the browser prompt by sending the password in a header:
x-site-password: my-secret-code
Claim Anonymous Site
POST/api/v1/publish/:slug/claim
{"claimToken": "abc123..."}
Converts an anonymous site to your account. Removes the 24-hour expiry. Requires auth + the claim token from the original create response.
Duplicate a Site
POST/api/v1/publish/:slug/duplicate
{"viewer": {"title": "New Title", "description": "Optional override"}}
Server-side copy to a new slug. Files are copied in S3 without re-upload. Optionally override the viewer title and description on the copy.
Response:
{
"slug": "new-slug-xyz",
"siteUrl": "https://new-slug-xyz.shipsite.co/",
"sourceSlug": "bright-canvas-a7k2",
"status": "active",
"currentVersionId": "01GHI...",
"filesCount": 5
}
Delete a Site
DELETE/api/v1/publish/:slug
Permanently deletes the site and all its files.
Version History ShipSite
GET/api/v1/publish/:slug/versions
List all deployed versions with timestamps and file counts. Each publish or update creates a new version.
{
"slug": "bright-canvas-a7k2",
"versions": [
{"versionId": "01DEF...", "createdAt": "2026-03-29T...", "fileCount": 5, "current": true},
{"versionId": "01ABC...", "createdAt": "2026-03-28T...", "fileCount": 3, "current": false}
]
}
Rollback ShipSite
POST/api/v1/publish/:slug/rollback
{"versionId": "01ABC..."}
Instantly revert to any previous version. No re-upload needed — the files are already in S3.
{
"success": true,
"slug": "bright-canvas-a7k2",
"siteUrl": "https://bright-canvas-a7k2.shipsite.co/",
"previousVersionId": "01DEF...",
"currentVersionId": "01ABC..."
}
Download as ZIP ShipSite
GET/api/v1/publish/:slug/download
Download the current version of a site as a ZIP archive. Useful for backups or migrating to another host.
curl -OJ -H "Authorization: Bearer sk_..." \
https://shipsite.co/api/v1/publish/bright-canvas-a7k2/download
Analytics ShipSite
GET/api/v1/publish/:slug/analytics?days=30
View traffic analytics for your site. Returns daily breakdowns of views, unique visitors, top paths, and top referrers.
Query params: days (1–90, default 30)
{
"slug": "bright-canvas-a7k2",
"period": {"startDate": "2026-02-27", "endDate": "2026-03-29", "days": 30},
"daily": [
{
"date": "2026-03-29",
"views": 142,
"uniqueVisitors": 89,
"topPaths": [["/", 98], ["/about.html", 44]],
"topReferrers": [["google.com", 34], ["twitter.com", 12]]
}
],
"totals": {"views": 4210, "uniqueVisitors": 1893}
}
Share via Email ShipSite
POST/api/v1/publish/:slug/share
{
"senderName": "Kent",
"recipientName": "Alice",
"recipientEmail": "alice@example.com",
"message": "Check out my new project!"
}
Send a branded email to someone with a link to your site. The recipient receives a styled email with your name, an optional personal message, and a prominent link to the site.
Request body:
| Field | Required | Description |
|---|---|---|
senderName | Yes | Your name (max 64 chars) |
recipientName | Yes | Recipient's name (max 64 chars) |
recipientEmail | Yes | Valid email address (max 254 chars) |
message | No | Optional personal note (max 500 chars) |
Response:
{"success": true, "slug": "bright-canvas-a7k2", "recipientEmail": "alice@example.com"}
422 and the email is not sent. Rate limited to 10 shares per hour per user.Limits
| Feature | Anonymous | Authenticated |
|---|---|---|
| Max file size | 25 MB | 5 GB |
| Max files per site | 20 | 500 |
| Site expiry | 24 hours | Permanent |
| Rate limit | 5/hour per IP | 200/hour |
| Version history | — | Full |
| Rollback | — | Any version |
| ZIP download | — | Current version |
| Analytics | — | 90-day retention |
| Auth rate limit | 10 requests/15 min per IP, 5 OTP attempts/15 min per email | |
| Share rate limit | — | 10 shares/hour per user |
Markdown Support
Markdown files (.md, .markdown, .mdown, .mkd) are automatically converted to styled HTML during finalization. The original .md file is preserved and a new .html version is added to the manifest. If no index.html exists, the first root-level .md file is converted to index.html so it serves at /.
Serving Rules
When a request hits <slug>.shipsite.co/:
- If
index.htmlexists, serve it - If only one file, serve that file
- Otherwise, show a directory listing
Subdirectory paths resolve to <path>/index.html if present.