Documentation
REST API v1 for static hosting workspaces. Base URL: https://ipfshost.io/api/v1
Static-only hosting
IPFSHost hosts static output only. Apps that need a running server, database, or PHP runtime must be exported to static or use an external backend.
Supported: HTML/CSS/JS, React/Vue/Astro/Vite builds, docs sites, ZIP exports. Not supported: PHP, MySQL, SSR without static export.
Quick start
Deploy a static site in three steps — no infrastructure expertise required.
- Sign up at onboarding or create a workspace.
- Deploy — public GitHub repo (Deploy) or ZIP with index.html (Upload).
- Share — use the preview URL immediately; connect a custom domain when ready.
Reliable static hosting with predictable deploys and rollback — no VPS to manage.
Git deploy (Dashboard)
Public GitHub or GitLab.com repo URL in Dashboard → Deploy — framework detection, isolated build, preview URL. Not available via REST API v1 (ZIP and file upload are). SSR/PHP runtimes are not supported.
Accepted URLs: https://github.com/owner/repo, https://gitlab.com/group/repo (nested groups supported), git@gitlab.com:group/repo.git. Self-hosted GitLab is not supported.
ZIP deploy
Upload a static build archive via dashboard or POST /api/v1/sites/deploy-zip.
Alpha limits
Build timeout, repo size, and output caps are listed under Build limits. Plan quotas apply per workspace.
Static compatibility matrix
See Frameworks — only static output (HTML/CSS/JS assets) is hosted.
Workspace registration
Creates a workspace (tenant), owner account, and an onboarding API key. Used by guided onboarding and by integrations — no API key required for this call.
POST /api/v1/onboarding/register — JSON body:
{
"name": "my-site",
"email": "you@example.com",
"password": "min-8-chars",
"slug": "optional-workspace-slug",
"invite_code": "alpha-optional-when-required"
}name— workspace / project label (required, max 64 characters after trim).email— owner login (required, unique per account).password— min. 8 characters (required).slug— optional URL-safe id; derived fromnameif omitted.invite_code— required when public alpha invite mode is enabled on the server.
Response 201 — example:
{
"ok": true,
"tenant": { "id": "…", "slug": "my-site", "name": "my-site" },
"user": { "id": "…", "email": "you@example.com", "role": "owner" },
"api_key": {
"id": "…",
"prefix": "ipfh_…",
"key": "ipfh_…full_secret_shown_once"
},
"dashboard_login": "/dashboard/login",
"steps": ["Save your API key — shown once", "…"]
}The onboarding key is created with scopes upload, deploy, and read. Store api_key.key immediately — it is not shown again.
Guided onboarding then signs in via POST /dashboard/auth/login (browser cookie). API-only clients can use the returned key without a session.
Errors: project_name_required, project_name_too_long (with max_length), Tenant slug already exists, Invite code required / Invalid invite code (when invites enabled), other validation as 400 with error message.
Guided onboarding
The wizard at /onboarding walks through project type, deploy method (GitHub, GitLab, ZIP, API, or docs), workspace registration, and first deploy.
- Guests must complete workspace registration (email + password) before Git/ZIP/API deploy steps — the UI calls
POST /api/v1/onboarding/registerthen opens the dashboard or deploy screen. - ZIP — upload at step 4 or use
POST /api/v1/sites/deploy-zipwith the onboarding key. - GitHub / GitLab — after registration, continue in Dashboard → Deploy (not available on REST API v1).
- API-only path — choose API in the wizard or skip the UI: register via this endpoint, then call v1 with
Authorization: Bearer …. - Documentation-only — selecting docs still shows the registration step, then returns here.
Shortcut: /onboarding?mode=zip pre-selects ZIP upload.
Dashboard session (browser)
Cookie-based auth for the web dashboard (separate from API keys). Base path: /dashboard/auth.
| Method | Path | Notes |
|---|---|---|
POST | /dashboard/auth/register | Same fields as onboarding register (+ optional invite_code); sets session cookie. |
POST | /dashboard/auth/login | JSON { "email", "password" } → session cookie. |
POST | /dashboard/auth/logout | Clears session cookie. |
GET | /dashboard/auth/me | Returns { ok, session } with tenant_id, email, role when logged in. |
POST | /dashboard/auth/resend-verification | Resend verification email (session or email in body). Cooldown 30s. |
GET | /dashboard/auth/verify-email?token=… | Email confirmation link (confirms on server, redirects to login). Legacy /dashboard/verify-email redirects here. |
POST | /dashboard/auth/forgot-password | Request reset link (+ captcha). JSON may include lang: en | ru. |
POST | /dashboard/auth/reset-password | Set new password with token from email. |
POST | /dashboard/auth/sync-locale | Save UI language (lang) for transactional emails (session required). |
GET | /dashboard/auth/oauth/providers | Lists enabled OAuth providers (GitHub, Google). |
Dashboard ZIP deploy: POST /dashboard/api/deploy-zip (multipart archive, session cookie) — see Deploy ZIP.
Register and forgot-password require a captcha (GET /dashboard/auth/captcha?purpose=register|forgot_password). Pass captcha_id, captcha_answer, and optional lang in the POST body.
Account & email
New workspaces must confirm the owner email before deploy, API keys, or custom domains. Password reset and security notices are sent from IPFSHost (noreply@ipfshost.io).
- Verification — after register or OAuth sign-up, open the link in the email (valid 24h). Until verified, the dashboard shows a resend gate.
- Language — emails use EN or RU based on the site language switcher,
langin auth requests, cookieipfshost_lang, orAccept-Language. Changing language in the dashboard updates stored preference. - OAuth —
GET /dashboard/auth/oauth/{provider}?mode=login|registerstarts GitHub or Google sign-in when configured. Email from the provider must be visible. - Workspace slug — the optional
slugat registration is an internal workspace address, not your public website domain. Custom hostnames are added later under Custom domains.
Authentication
Send your API key in the Authorization header (or use a session cookie only for dashboard HTML/API under /dashboard/api, not for /api/v1).
Authorization: Bearer ipfh_your_secret_key
Or header X-API-Key: ipfh_…. Raw key is shown once at creation. Verify a key with GET /api/v1/me (scope read).
curl -sS "https://ipfshost.io/api/v1/me" \ -H "Authorization: Bearer $IPFSHOST_API_KEY"
Upload file
POST /api/v1/files/upload — multipart file, optional name.
curl -X POST "https://ipfshost.io/api/v1/files/upload" \ -H "Authorization: Bearer $IPFSHOST_API_KEY" \ -F "file=@./document.pdf" \ -F "name=document.pdf"
{
"ok": true,
"site": { "id": "…", "release_id": "…", "status": "live", "preview_url": "https://…" }
}
Deploy ZIP site
POST /api/v1/sites/deploy-zip — multipart field file (.zip), optional name (display name, max 64 chars). Requires index.html at site root or in dist/.
Dashboard ZIP upload uses field archive on POST /dashboard/api/deploy-zip — different from API v1.
curl -X POST "https://ipfshost.io/api/v1/sites/deploy-zip" \ -H "Authorization: Bearer $IPFSHOST_API_KEY" \ -F "file=@./dist.zip" \ -F "name=my-site"
POST /api/v1/sites/:id/redeploy-zip — same multipart shape (file, optional name).
List sites & preview URLs
GET /api/v1/sites — each site includes preview_url, gateway_url, and urls (stable for the active release CID). GET /api/v1/sites/:id for one site. Preview links are not one-time tokens — they stay valid while the release is published.
curl -sS "https://ipfshost.io/api/v1/sites" \ -H "Authorization: Bearer $IPFSHOST_API_KEY"
Platform status
GET /api/status — public metrics. trust.deploys_today counts ZIP publishes + Git builds today; trust.storage_nodes_total is delivery fleet size; trust.nodes_online is platform workers. See metric_hints in the JSON.
Quotas
GET /api/v1/quota returns storage, objects, bandwidth usage vs soft/hard limits. Exceeding hard limits returns 402 with quota_exceeded.
Error format
| Code | HTTP | Meaning |
|---|---|---|
auth_required | 401 | Missing API key |
scope_denied | 403 | Key lacks scope |
quota_exceeded | 402 | Hard quota hit |
rate_limited | 429 | Too many requests |
project_name_required | 400 | Empty workspace name (onboarding) |
project_name_too_long | 400 | Name longer than 64 characters |
Tenant slug already exists | 400 | Pick another slug or name |
Invite code required | 400 | Alpha invite mode — send invite_code |
Invalid invite code | 400 | Unknown or exhausted invite |
{ "ok": false, "error": "Human message", "code": "quota_exceeded" }Scopes
| Scope | Access |
|---|---|
read | List files, sites, quota |
upload | Upload files |
deploy | Deploy ZIP sites |
admin | API keys, settings (workspace admin) |
Rate limits
Per-workspace API RPS and per-key windows apply. Response 429 includes Retry-After header (seconds).
Git deploy flow
1. Dashboard → Deploy — paste GitHub or GitLab.com URL 2. Auto-detect framework (Vite, Next static export, Astro, …) 3. Build → preview URL 4. Dashboard → Domains → verify DNS records
Framework matrix (static-only)
| Preset | Build | Output | Notes |
|---|---|---|---|
| Angular | npm run build | dist/browser/ | Browser build only — no SSR server on IPFS; base href="./" |
| React / Vue (Vite) | npm run build | dist/ | SPA — API on separate origin |
| Next.js static | npm run build | out/ | Requires output: export |
| Astro / Nuxt / Gatsby | SSG command | dist/ / .output/public | No SSR on IPFS |
| WordPress | — | static export | Simply Static / WP2Static ZIP only |
| ZIP upload | — | — | ZIP onboarding |
IPFS hosts immutable static snapshots — not PHP, Node servers, or SSR runtimes.
DNSLink
Point your domain to immutable content via TXT record:
_dnslink.example.com TXT dnslink=/ipfs/<release-id>
After deploy, use Dashboard → Domains to add your hostname, copy TXT instructions, and run verify.
Custom gateway
Serve https://yourdomain.com via gateway.ipfshost.io (or your configured PUBLIC_GATEWAY_URL):
- DNSLink — _dnslink.example.com TXT dnslink=/ipfs/<release-id>
- CNAME — point www to your preview hostname
- nginx — proxy_pass to /ipfs/<release-id>/
Preview URLs: https://preview.ipfshost.io/ipfs/<release-id> (see PUBLIC_PREVIEW_URL).
Build limits
| Limit | Default |
|---|---|
| Build timeout | BUILD_MAX_SECONDS=300 |
| Repo clone size | BUILD_MAX_REPO_MB=200 |
| Output size | BUILD_MAX_OUTPUT_MB=500 |
| Log lines | BUILD_MAX_LOG_LINES=5000 |
Preflight checks (git, node, npm, disk) run before each build. Long-running dev servers are blocked.
SPA routing on IPFS
Gateways serve files by path. Browser history routes (/about) need either:
- Hash router —
#/about(works everywhere) - Gateway fallback — nginx/Cloudflare serves
index.htmlfor unknown paths - Vite base —
base: './'for relative assets
React (Vite)
npm run build → dist/. Use HashRouter or configure gateway SPA fallback. Avoid absolute /assets/ URLs.
Vue (Vite)
Same as React — base: './' in vite.config, history mode needs gateway fallback.
Astro
Static output only. Large content collections increase build size — trim assets if output exceeds limits.
Common deployment fixes
Symptom → cause → fix for the most common Dashboard deploy failures.
| Symptom | Likely cause | Fix |
|---|---|---|
| Multiple index.html | Monorepo root / Static HTML preset | React+Vite preset + tree URL to app folder, or npm run build → dist/ |
| Git clone failed | Wrong repo URL or private repo | Public repo; monorepos: /tree/branch/path |
| No build / raw repo published | Static HTML preset on app repo | Match preset to detection (Vite → React/Vite) |
| Routes 404 | SPA history mode | Hash router or gateway fallback |
| Blank page | Wrong asset base | base: './' in Vite |
| npm exit 1 | Install/build error | Run npm ci && npm run build locally |
| npm ci EUSAGE | No package-lock.json | Redeploy (auto npm install) or commit lockfile |
| Daily build limit | Builds today ≥ plan | Wait UTC midnight or Dashboard → Usage |
| SSR error | Next server code | output: 'export' |
| DNS verify fails | TXT not propagated | Wait TTL, grey-cloud TXT on Cloudflare |
Troubleshooting builds
Build-time issues on the Git worker. Anchor: #troubleshooting.
- Multiple index.html — monorepo root with Static HTML preset.
- Git clone / auth — public repos only; use tree URLs from templates.
- Wrong framework preset — align preset with detection on step 2.
- Preflight fail — install git/node/npm on the host or pick a simpler preset.
- Timeout — reduce dependencies or split build; default 300s.
- Output missing — check preset output dir (
dist,out, …). - DNSLink verify — wait for TXT propagation; disable CF proxy on TXT.
- No verification email — check spam; wait 2 minutes before resend; link expires after 24h.
Node.js & Python
const fd = new FormData();
fd.append("file", fileBlob, "file.txt");
const res = await fetch("https://ipfshost.io/api/v1/files/upload", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.IPFSHOST_API_KEY}` },
body: fd,
});
if (!res.ok) throw new Error((await res.json()).error);
const { site } = await res.json();import os, requests
r = requests.post(
"https://ipfshost.io/api/v1/files/upload",
headers={"Authorization": f"Bearer {os.environ['IPFSHOST_API_KEY']}"},
files={"file": open("file.txt", "rb")},
)
r.raise_for_status()
print(r.json()["site"]["cid"])