Contract-first REST for builders and agents. Same control plane as Fat Heron products — documented here for the developer surface.
Create sanitized static HTML and assign it as the apex (or /buy) lander on a portfolio domain. Seller-key path — no portal clicks.
Required scopes: landers:write, domains:read · Base https://api.fatheron.dev
If the user already gave you an fh_live_ or fh_test_ key, use it now. Do not send them to the portal. Landers are static sanitized HTML: scripts, arbitrary iframes, and event handlers are stripped. {{domain}} is replaced with the request Host at serve time. The account needs a seller / broker / domainer / admin role, and the domain must already be in GET /v1/portfolio/domains.
Already have a key? Execute over HTTP. You run the calls. Do not send the human to the portal or ask them to paste curl output.
1. List portfolio domains
GET /v1/portfolio/domains X-API-Key: fh_live_…
Find domain_id for the hostname. If the name is missing, stop — you cannot assign a lander to a domain this account does not own.
2. List existing landers
GET /v1/portfolio/landers X-API-Key: fh_live_…
Reuse an lt-… id if a template already exists. Check apex_lander_template_id on the domain.
3. Create a lander template
POST /v1/portfolio/landers
X-API-Key: fh_live_…
Content-Type: application/json
{
"name": "example.com apex",
"html": "<!DOCTYPE html><html><head><meta charset=\"utf-8\"/><title>{{domain}}</title></head><body><h1>{{domain}}</h1><p>This name is for sale.</p></body></html>"
}201 with id like lt-…. Keep that id. Confirm the response html still has your markup (scripts/iframes will be gone).
4. Assign to apex
POST /v1/portfolio/landers/assign
X-API-Key: fh_live_…
Content-Type: application/json
{
"domain_ids": [
"<domain_id>"
],
"apex_lander_template_id": "lt-…"
}updated: 0 means the domain_id is not owned by this account. Clear apex with clear_apex: true. Sale page: sale_lander_template_id.
5. Verify public serve
GET /public/v1/apex-lander Host: example.com
No API key. Host must be the domain. 200 + HTML means the assignment is live. No assignment → park fallback, not this body.
invalid_api_key
The key is wrong or revoked. Stop. Do not send the human to mint a replacement unless they ask.
insufficient_scope
Need landers:write (or domains:* / legacy write). Say that — do not invent a portal mint flow.
seller_role_required
Account is not seller / broker / domainer / admin. The key is fine; the account cannot own landers.
template_not_found / apex_template_not_found
List landers and use an lt- id from this account, or create a new template.
assign updated: 0
domain_id is not in this account’s portfolio. Re-list GET /v1/portfolio/domains.
script / iframe stripped
Expected. Use static markup and outbound links. Do not retry with widgets.