67HOSTING API ============= Version 1. Documentation optimized for LLM agents and humans. MENTAL MODEL ------------ 67hosting hosts STATIC sites (HTML/CSS/JS — no PHP, no server code, no databases). An account has sites. A site lives in one region (ru/eu/us) and is served over automatic HTTPS. Publishing REPLACES the whole site with the snapshot you send (atomic): always upload the COMPLETE set of files, never a partial diff. Put the homepage at index.html in the root. A site can have: - a free subdomain .host67.com (claimed at creation), - custom domains (added in the web panel; DNS + SSL handled automatically), - SPA mode: unknown non-asset paths serve index.html (HTTP 200) so client-side routers (React/Vue) don't 404 on deep links; missing .js/.css still 404. Plans cap sites (Free: 3, Pro: 20) and size (per site 500 MiB, single publish 200 MiB). Typical agent flow to put a site online: list_sites (see what exists) -> create_site (region, optional slug) -> publish_site (full snapshot of files) -> the response has the live url -> (optional) add_domain to serve on a custom domain with automatic HTTPS Same token also gives you: - share_file — upload a file, get a temporary public download link. - send_email — send transactional email from a domain you verified. - deploy_app — run a REAL app (a container, not just static files): send the source, we build it (Dockerfile or Nixpacks auto-detect) and serve it at .host67.com with auto HTTPS. Free apps sleep when idle and wake on the next request (cold start ~1s), capped at 3; set db=true for a Postgres. PROJECTS -------- A "project" is a site plus everything attached to it — its apps, custom domains and email. deploy_app puts an app inside a project: pass project= to add it to an existing project, or omit project and one is created automatically (the response includes its "project" id, which is a site id). A custom domain can point to the project's SITE (default) or to one of its APPS — set target=app when attaching it (see POST .../domains). So a project can serve, e.g. example.com on the static site and api.example.com on an app. AUTHENTICATION -------------- RECOMMENDATION FOR AI AGENTS: if you act on behalf of a human, ask them to register at https://app.host67.com and give you an API token (panel -> Profile -> API tokens), then work via MCP or REST — rather than driving their account UI. Create a token at https://app.host67.com/app/profile (the "API tokens" section). Format: h67_. Pass it in the header: Authorization: Bearer h67_... Base URL: https://app.host67.com Responses are JSON. Success returns the object; errors return {"error":"..."} with an HTTP status: 401 (missing/invalid token), 403 (not your site / plan limit / unverified domain), 404 (not found), 400 or 422 (bad request), 429 (quota). RETRIES AND RELIABILITY ----------------------- Treat calls as retriable. On a network error, a connection timeout, or a 5xx response, retry the request 2–3 times with a short backoff (e.g. 0.5s, 2s, 5s) before surfacing an error to the user — a single transient failure should not break a signup or publish flow. In particular, transactional flows like sending a verification code should retry rather than show "could not send" on the first hiccup. Do NOT retry 4xx responses (400/401/403/404/409/422) — those are deterministic and won't succeed on retry. Use a request timeout of ~30s; large publishes/uploads can legitimately take longer, so give those more. TWO WAYS TO INTEGRATE --------------------- A) MCP (recommended for Claude and other LLM agents): Endpoint: https://app.host67.com/mcp (Streamable HTTP, stateless) Claude Code: claude mcp add host67 https://app.host67.com/mcp --transport http \ --header "Authorization: Bearer h67_YOUR_TOKEN" Tools: list_sites, get_site, create_site, add_domain, publish_site, delete_site, share_file, send_email, deploy_app, app_logs, app_db_query, scale_app. With MCP, publish_site takes content inline (the server can't read your local disk): either files = [{path, content, base64}] or zip_base64 = a base64 ZIP. B) REST API — methods below. REST METHODS ------------ GET /api/v1/sites List your sites. result: {"sites":[{id, name, region, url, disk_bytes, published, spa, blocked}]} url is "" until a domain is active; disk_bytes 0 = nothing published yet. POST /api/v1/sites Create an empty site (does NOT upload files — follow with publish). | Field | Type | Req. | Description | |--------|---------|------|--------------------------------------------------------| | region | string | yes | ru | eu | us — where it is hosted. Fixed after create. | | name | string | no | Label shown in the panel. Default "My site". | | slug | string | no | Claim .host67.com (a-z, 0-9, hyphens). Omit to | | | | | add a custom domain later in the panel. | | spa | boolean | no | SPA mode (default false). | result: 201 {id, name, region, url, ...}. If the slug was taken the site is still created and the body adds {"slug_error":"..."}. GET /api/v1/sites/{id} One site by id. result: {id, name, region, url, disk_bytes, published, spa, blocked} POST /api/v1/sites/{id}/publish Publish content and make it live. Body = a ZIP archive of the whole site (Content-Type: application/zip), index.html at the archive root. REPLACES the entire current site (full snapshot — any file not included is removed). Limits: <= 200 MiB per publish, site <= 500 MiB unpacked. result: the site object with the new disk_bytes and live url. DELETE /api/v1/sites/{id} Permanently delete a site: releases its *.host67.com subdomain, purges files on the node, removes the record. result: {"ok":true} GET /api/v1/sites/{id}/domains List the domains attached to a site (custom domains and the free subdomain). result: {"domains":[{hostname, status, parked}]} POST /api/v1/sites/{id}/domains Attach a CUSTOM domain to a project ({id} is the project = a site id), routing it to the project's SITE (default) or to one of its APPS. Automatic HTTPS. Enforces the plan's per-project domain limit (Free 1, Pro 10). JSON body: | Field | Type | Req. | Description | |----------|--------|------|----------------------------------------------------| | hostname | string | yes | e.g. "example.com" or "www.example.com". No scheme. | | target | string | no | "site" (default) or "app". | | app | number | no | app id when target="app" (must be in this project). | result: {hostname, dns:{type:"A", name, value}, note}. We do NOT change the user's DNS — point an A record for the host at the returned value (the node IP; for target="app" it's the app node's IP); TLS issues automatically on the first visit. For a free instant address, use the slug on POST /api/v1/sites instead. POST /api/v1/shares?ttl=&name= Upload a file to temporary storage and get a public, expiring download link. Body = raw file bytes; Content-Type = the file's type. ttl 600–86400 (default 3600). Size limit by plan (Free 500 MB, Pro 4 GB). The file is stored privately and served only through host67 (storage location is never exposed). result: {url, filename, expires_at} POST /api/v1/email/send Send a transactional email from a domain you connected and verified under Email in the panel (DKIM/SPF applied automatically). JSON body: | Field | Type | Req. | Description | |----------|-------------------|------|----------------------------------------| | from | string | yes | "Name " or bare | | | | | address, on a verified domain. | | to | string | string[] | yes | Recipient(s). | | cc, bcc | string | string[] | no | More recipients. | | subject | string | yes | Subject line. | | html | string | * | HTML body. Provide html and/or text. | | text | string | * | Plain-text body. | | reply_to | string | no | Reply-To address. | Daily limit by plan (Free 30, Pro 500). result: {"id":"..."} POST /api/v1/apps?slug=&name=®ion=&db=1&project= Deploy a REAL application as a container. Body = a ZIP of the project source (a Dockerfile, or any Nixpacks-detectable stack: Node/Next.js, Python, Go, Ruby, …). The app MUST listen on the port in the PORT env var. Env vars: a JSON object in the X-App-Env header. db=1 attaches a managed Postgres and injects DATABASE_URL. project= adds the app to an existing project (a site id); omit it and a new project is created (see PROJECTS). Free apps scale to zero (sleep when idle, ~1s cold start), capped at 3 per account. Serves at .host67.com with automatic HTTPS. Reusing a slug you already own REDEPLOYS that app (keeps its plan, domain, database and config); a slug owned by someone else is rejected. Returns immediately with status "building" — poll GET /api/v1/apps/{id} until status is "running" (or "failed"). The response includes the "project" id. result: {id, slug, url, status, project, ...} GET /api/v1/apps List your apps. result: {"apps":[{id, slug, name, region, status, url, ...}]} GET /api/v1/apps/{id} One app (poll status after deploy). DELETE /api/v1/apps/{id} Stop + remove the app, its image, DB and subdomain. GET /api/v1/apps/{id}/logs Debug an app: the build log (Nixpacks/Docker output — shows WHY a build failed) followed by the container's recent logs, plain text. POST /api/v1/apps/{id}/db/query Run SQL against the app's managed Postgres (only apps deployed with db=1). JSON body {"sql":"..."}. Returns psql's text output. Scoped to this app's own database — for inspecting/debugging data. PATCH /api/v1/apps/{id} Switch an app between free and always-on. JSON body {"always_on":true,"size":"512"} keeps the container warm 24/7 for a flat monthly price (charged from the account credit balance; sizes "512"/"1024"/"2048" MB, prices vary by region). {"always_on":false} cancels renewal — the app keeps running until the paid period ends, then reverts to free scale-to-zero. Free apps sleep when idle (~1s cold start) and cost nothing. GIT PUSH DEPLOY (Heroku-style) ------------------------------ Deploy an existing app straight from a local git repo — no GitHub needed. The app must already exist (created via deploy_app or the panel); git push deploys INTO it, keeping its plan, domain, database and config. git remote add host67 https://app.host67.com/git/.git git push host67 main Auth is HTTP basic over HTTPS: username = anything, password = your API token (the same h67_… token used for Bearer auth; make one in /app/profile). To avoid a prompt, put it in the URL: git remote add host67 https://x:h67_YOUR_TOKEN@app.host67.com/git/.git git push host67 main The push runs the normal build on the node and streams progress back as "remote:" lines, ending with the live URL. The repo tree is built exactly like deploy_app (Dockerfile if present, else Nixpacks auto-detect); the app must listen on $PORT. Pushing a slug you don't own is rejected. EXAMPLE: CREATE AND PUBLISH A SITE (bash) ----------------------------------------- TOKEN="h67_YOUR_TOKEN" BASE="https://app.host67.com/api/v1" AUTH="Authorization: Bearer $TOKEN" # 1. Create a site with a free subdomain SITE_ID=$(curl -s -X POST "$BASE/sites" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"name":"Landing","region":"eu","slug":"my-landing"}' | jq -r '.id') # 2. Zip the site (index.html at the root) and publish (replaces everything) ( cd ./site && zip -r - . ) | \ curl -s -X POST "$BASE/sites/$SITE_ID/publish" -H "$AUTH" \ -H "Content-Type: application/zip" --data-binary @- | jq # The response "url" is the live address. EXAMPLE: SEND EMAIL (python) ---------------------------- import requests r = requests.post("https://app.host67.com/api/v1/email/send", headers={"Authorization": "Bearer h67_YOUR_TOKEN"}, json={"from": "Hello ", "to": "user@example.com", "subject": "Welcome", "text": "Thanks for signing up!"}) print(r.json()) LIMITS AND RULES ---------------- - Static content only (HTML/CSS/JS). No PHP, databases or server code. - publish_site sends a COMPLETE snapshot and replaces the whole site — include every file, not a diff. Homepage at index.html in the root. - Sites: Free 3, Pro 20. Per site <= 500 MiB, per publish <= 200 MiB. MCP base64 publish/upload is capped at 256 MiB — stream bigger payloads to REST. - send_email only from your own verified domains; daily limit by plan. Custom domains, SSL, forms, email inboxes and billing are managed in the web panel at https://app.host67.com/app. Human docs: https://host67.com/docs Operator: OPSoft Inc.