Start for free. Sign up without a card and you'll get a test project and a test key right away. Once your company (or, for a personal account, you) is verified, you can create a live key in the portal.
Quickstart
Hypamoon is the infrastructure your chat builds on for a business. Your site goes live on a real address or your own domain, your forms keep every message, and when you want more, the same project adds an app with sign-in, a checkout, email from your domain, a phone number that answers and agents. Your chat reaches it through MCP, your code through the REST API; both work on the same project.
Sign up at https://app.hypamoon.com with your email address. It's free, needs
no card, and gives you a test project and a test key right away. Then pick
your track: from your chat, without code, or
from code, with curl. Both end with a site that has a working
contact form.
From your chat
No code, no hosting, no form service. You talk, and your chat does the work.
1. Connect Hypamoon once
Add Hypamoon to Claude, ChatGPT or Grok as a connector and sign in. It takes a minute: Connect your chat shows every click. In ChatGPT it is Plugins, Add, Add custom MCP server, also on the free plan. Or download the plugin archive and upload it in ChatGPT or Claude. Grok connectors work on every Grok plan, the free one included. Claude Code, Cursor and Grok Build sign in the same way; Lovable uses a key for the time being (see MCP). No account yet? You'll create one as you sign in.
2. Publish your site
Your chat asks for what it needs, writes the page and publishes it. You'll get a link
like https://acme-bakery.hypamoon.app that works on any phone. Want a
change? Say it ("make the opening hours bigger") and the link stays the same.
3. Add a contact form
Every message is saved in your project, with a bot check in front of the form. In live mode it also lands in your inbox, and your reply goes straight to the visitor. In your test project the messages are saved, but no email is sent.
4. Read what came in
Your chat reads the messages, newest first, and tells you who wants what. Ask a follow-up like "only this week" or "mark Ann's question as answered".
5. Choose where it lives
Your chat connects a domain you already own and copies its existing records first, so your email keeps working. Then it tells you the two nameservers to set at your registrar, and keeps checking until the site is live on your domain.
No domain? Pick how people find the site:
- Public: on its readable address, for everyone. "Change the address of my site to acme-bakery."
- Unlisted: only on a secret link, kept out of search engines. "Make my site unlisted."
- Password: one shared password for every visitor. "Put a password on my site so only my team can see it."
What next
- A number that answers. "Give my business a number that rings me first and lets the receptionist pick up when I can't." See Phone, SMS and chat.
- Agents. "When a new request comes in, email me a short summary." An agent follows your instructions when something happens and asks you before it contacts anyone. See Agents.
- Apps. Server code under your site's
/apipath, with your data and secrets within reach. See Apps. - Versions. Every change to your site is kept, on every plan; "go back to yesterday's version" puts it back, and "put app.js back the way it was" restores just that file. If an agent tries to replace most of your site in one go, the change waits as a preview until you say yes. See Pricing for how long versions are kept.
- Payments: a checkout on your own site, paid into your own account. See Payments.
Say this has more sentences that work today.
From code
The same steps with the REST API. Every call below is a real path with real field names; the API reference has every option.
- The base URL is
https://api.hypamoon.com/api/v1. $HYPAMOON_API_KEYis your key. Keep it on your server, never in a page.- Every error is
application/problem+jsonwith atypeURL that explains the fix, likehttps://hypamoon.com/docs/errors/version_conflict.
1. Get your test key
Right after you sign up, the portal shows your first test key (hm_test_...)
once. Store it as a secret. It belongs to your test project: it has every
scope, never costs anything and needs no payment method. Sites and data are
real in test mode too, so your site is public right away; email, calls and SMS
are simulated.
Your live project sits alongside it. Once your company (or, for a personal
account, you) is verified under Verification, you create a live key
(hm_live_...) yourself in the portal under API keys. Your code stays the same; only
the key changes. See Test mode and
Authentication.
curl https://api.hypamoon.com/api/v1/project \
-H "Authorization: Bearer $HYPAMOON_API_KEY"The response says "mode": "test" and shows your project.
2. Publish a site
Send the files the browser loads: at least /index.html, plain HTML, CSS and
JavaScript. There is no build step, so send built output, never React or Vite
source.
curl -X POST https://api.hypamoon.com/api/v1/sites \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Bakery",
"slug": "acme-bakery",
"files": [
{ "path": "/index.html", "content": "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>Acme Bakery</title><link rel=\"stylesheet\" href=\"/style.css\"></head><body><h1>Acme Bakery</h1><p>Fresh bread every morning from 7.</p></body></html>" },
{ "path": "/style.css", "content": "body { font-family: system-ui, sans-serif; margin: 2rem; }" }
]
}'{
"id": "6a7b8c9d-1e2f-4a5b-8c7d-9e0f1a2b3c4d",
"slug": "acme-bakery",
"name": "Acme Bakery",
"url": "https://acme-bakery.hypamoon.app",
"files": [
{ "path": "/index.html", "content_type": "text/html", "bytes": 296, "updated_at": "..." },
{ "path": "/style.css", "content_type": "text/css", "bytes": 58, "updated_at": "..." }
],
"version": "k3Q9vXbT2mLc8Rw1",
"password_protected": false,
"test": true,
"taken_offline": null,
"...": "..."
}The site is live at url as soon as the call returns. Leave out slug and we'll
make one from name; to check first, GET /sites/address-suggestions?name=Acme%20Bakery
suggests available ones. Images and fonts work best as
"source_url": "https://...", which we fetch once.
Change it with PUT /sites/{id}; the URL stays the same. Send
"keep_existing": true, the current version as "if_match_version", and
only the files you add or change (delete_paths removes files). If someone
changed the site in the meantime, you'll get 409 version_conflict with
current_version, and nothing is changed.
Coming from another static host? Send your redirects and headers along as two plain-text files.
/_redirects has one rule per line: from, to, and a status (301, 302, 307 or
308; 301 if you leave it out). To is a path on your site or a full https://
URL. A * at the end of from matches everything under it, and :splat puts
that part in the path of to. The domain of an outside URL is always the one in
the file; nothing a visitor types can change it, and the query is not passed on.
/challenge https://appchallenge.hypamoon.com 301
/docs/* https://docs.example.com/:splat 301
/blog/old/* /blog/:splat 308
/?p=12 /my-post/ 301/_headers sets response headers per path, like Netlify: a path line, then the
headers indented below it. Allowed: Content-Security-Policy,
X-Frame-Options, Referrer-Policy, Permissions-Policy, Cache-Control (up
to a year), Link, Strict-Transport-Security without preload,
Access-Control-Allow-* (not on /api) and your own X- headers. Not
allowed, and deploy_site names the line: Set-Cookie, Location,
Content-Type, Content-Length, anything under /_hypamoon/. At most 100
paths and 300 header lines.
/*
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=()
/_next/static/*
Cache-Control: public, max-age=31536000, immutableYour Content-Security-Policy is applied next to ours (ours only limits where
forms may post), and a password-protected site always stays private, no-store.
A Next.js static export (output: "export" in next.config) deploys as it
is: send the files in out/, /_next/ included, without assetPrefix. Route
handlers, middleware and server rendering do not export; put that code in an
app on /api. A path may use letters, digits and . _ ~ -, so leave out the
__next.*.txt prefetch files whose names hold other characters (like ! for a
route group): links then load the full page, which works the same. A webpack
build can name chunks after a route like [slug]; build with Turbopack (the
default since Next 16), or leave those files out.
3. Add a contact form
A form needs two things: a collection that accepts entries, and your project's public token.
curl -X POST https://api.hypamoon.com/api/v1/collections \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "contact",
"access": { "public_write": true },
"triggers": { "email_on_insert": true, "webhook_on_insert": true },
"schema": {
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"email": { "type": "string", "format": "email" },
"message": { "type": "string", "minLength": 1, "maxLength": 5000 }
},
"required": ["name", "email", "message"]
}
}'A collection is private until you say otherwise; public_write lets a form on
your site add entries, behind a bot check and limits. email_on_insert emails
every entry to your billing email address (live only), and webhook_on_insert
sends document.created to your webhooks. The schema is
optional; an entry that does not match is rejected with 422 schema_mismatch,
naming the field that is wrong.
The public token is not a secret: it goes in the page, and only works from the origins you list.
curl -X POST https://api.hypamoon.com/api/v1/chat-tokens \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Acme Bakery site", "allowed_origins": ["https://acme-bakery.hypamoon.app"] }'The response includes token (hm_pubtest_... with a test key). Put it in the form
and add the form to index.html with another PUT /sites/{id}:
<form data-hypamoon-collection="contact" data-hypamoon-token="hm_pubtest_..." data-hypamoon-success="Thanks, we received your message.">
<label for="name">Name</label>
<input id="name" name="name" required>
<label for="email">Email</label>
<input id="email" name="email" type="email" required>
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
<input type="text" name="website" data-hypamoon-honeypot tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-9999px">
<button type="submit">Send</button>
</form>
<script src="https://api.hypamoon.com/form.js" defer></script>form.js adds the bot check, sends the fields, and shows the text from
data-hypamoon-success. The name of each input becomes a field of the entry.
A bot that fills in the hidden website field gets a thank you, and nothing is
stored.
4. Read what came in
curl https://api.hypamoon.com/api/v1/collections/contact/documents \
-H "Authorization: Bearer $HYPAMOON_API_KEY"{
"data": [
{
"id": "be97ef66-6c17-491a-873f-ce173f8f3c87",
"collection_id": "c27449ad-8fad-4125-a852-afbde676bdef",
"data": { "name": "Ann Example", "email": "ann@example.com", "message": "Do you bake gluten-free bread?" },
"source": "public",
"created_at": "2026-10-06T16:37:26.605Z",
"updated_at": "2026-10-06T16:37:26.605Z",
"test": true
}
],
"has_more": false,
"next_cursor": null
}Entries come newest first; follow next_cursor while has_more is true. Mark an entry as
handled and list only those:
curl -X PATCH https://api.hypamoon.com/api/v1/collections/contact/documents/be97ef66-6c17-491a-873f-ce173f8f3c87 \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "merge": { "status": "answered" } }'
curl -G https://api.hypamoon.com/api/v1/collections/contact/documents \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
--data-urlencode 'filter={"status":"answered"}'Testing without a browser? POST /collections/contact/documents with
{ "data": { ... } } adds an entry with your key ("source": "api").
5. Choose where it lives
Your own domain. Connect a domain you already own and put the site on it:
curl -X POST https://api.hypamoon.com/api/v1/domains \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "example.com", "site_id": "6a7b8c9d-1e2f-4a5b-8c7d-9e0f1a2b3c4d" }'We copy the existing records first, email included, and respond with the two
nameservers to set at your registrar and the next_step. Then
POST /domains/{id}/activate checks whether they are in place and puts the
site on the domain and on www once they are. Can't change the nameservers? Send
"method": "cname" for www only. Looking for a new domain? Use
GET /domains/check?domain=example.com.
Unlisted. Only on a secret link, kept out of search engines:
curl -X PUT https://api.hypamoon.com/api/v1/sites/6a7b8c9d-1e2f-4a5b-8c7d-9e0f1a2b3c4d/address \
-H "Authorization: Bearer $HYPAMOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "visibility": "unlisted" }'The response has the secret link in url; "visibility": "public" brings the
readable address back, and "slug" renames the site (the old address keeps
redirecting for 90 days; at most 3 renames per site per 30 days). A change of address gives the site a new version, so read it
again before your next PUT /sites/{id}.
Password. A secret link is not a login. For a site only your team may see,
send { "password": "spring-tour-2027" } with PUT /sites/{id}: every visitor
enters it once per browser. "password": null opens the site again.
6. Add a phone number
Same project, same key. Phone, SMS and chat takes you from here to a number that rings your team first and lets a voice AI pick up when nobody answers, with a webhook for every call. Calls and Webhooks describe what you get back.
Going live
When your test run works, finish the verification in the portal at https://app.hypamoon.com, create a live key under API keys and swap it in. The live project starts empty: deploy your site and create your collection there again with the live key. What a plan includes is on Pricing.
Let your coding agent do it
Paste this into Claude Code, Cursor or Lovable, with your test key in the
environment as HYPAMOON_API_KEY:
Publish this site with the Hypamoon API (base URL https://api.hypamoon.com/api/v1,
auth "Authorization: Bearer $HYPAMOON_API_KEY"; never put the key in client code).
1. POST /sites with { name, slug, files }: the built output, at least /index.html.
Save the returned id and version.
2. POST /collections with { name: "contact", access: { public_write: true },
triggers: { email_on_insert: true }, schema } for name, email and message.
3. POST /chat-tokens with { name, allowed_origins: [<the site url>] } and use its
token in a form with data-hypamoon-collection="contact" and
data-hypamoon-token, plus <script src="https://api.hypamoon.com/form.js" defer>.
4. PUT /sites/{id} with keep_existing: true, if_match_version: <version> and only
the changed files.
5. Show me how to read the entries: GET /collections/contact/documents.
Every error is application/problem+json. Its "type" is a URL that explains the fix;
read it and follow it before trying something else.