Encited logo
Pricing

Domains API

Manage domains programmatically: list, add, remove, test connection, and update settings.

Manage your connected domains programmatically: list, add, remove, test the connection, and update per-domain settings. Base path: /api/domains.

Endpoints that accept a JSON body validate it against a schema; a malformed body is rejected with a default 400 validation response whose shape differs from the { "error": ... } bodies documented below.

Available on Business and Enterprise plans. Other plans receive 403 { "error": "plan_required", "requiredPlan": "business" }. Authenticate with an API key via x-lovablehtml-api-key: <API_KEY> or Authorization: Bearer <API_KEY>.

Key Scopes

API keys can be account-wide (unscoped) or scoped to a single domain. Which keys an endpoint accepts:

Endpoint Account-wide key Domain-scoped key
List, add, remove domains Yes No — 403 { "error": "requires_unscoped_key" }
Get, check, settings, sitemap Yes Yes, for the key's own domain only

Using a key scoped to a different domain returns 403 { "error": "api_key_domain_scope_mismatch" } (or domain_not_owned). An account-wide key targeting a domain not on your account returns 404 { "error": "not_found" }.

List Domains

GET/api/domains

List all domains on your account, ordered by creation date. Requires an account-wide key.

Response Body

json
CopyDownload
{
"domains": [
{
"id": "a1b2c3d4-e5f6-...",
"domain": "your-app.com",
"originHost": "my-app.lovable.app",
"status": "connected",
"sitemapUrl": "https://your-app.com/sitemap.xml",
"createdAt": "2026-01-15T09:30:00.000Z",
"updatedAt": "2026-02-01T12:00:00.000Z"
}
]
}

status is one of pending, connected, or via api; it can also be suspended for operator-locked domains. The same values apply to the detail object below.

Example

javascript
CopyDownload
const response = await fetch("https://encited.com/api/domains", {
headers: {
"x-lovablehtml-api-key": "<API_KEY>",
},
});
const data = await response.json();
console.log(data.domains);
bash
CopyDownload
curl -X GET \
"https://encited.com/api/domains" \
-H "x-lovablehtml-api-key: <API_KEY>"

Add a Domain

POST/api/domains

Connect a new domain to your account. Returns the DNS records to configure. Requires an account-wide key.

Request Body

Parameter Type Description
domainrequired string Domain to connect (e.g., "your-app.com" or "app.your-app.com").
originHost string Host where your SPA is published (e.g., "my-app.lovable.app"). Must differ from the domain and must not be an editor URL.

Response Body

json
CopyDownload
{
"id": "a1b2c3d4-e5f6-...",
"domain": "your-app.com",
"status": "pending",
"records": [
{ "type": "A", "name": "@", "value": "203.0.113.10", "ttl": 300 },
{ "type": "A", "name": "www", "value": "203.0.113.10", "ttl": 300 }
],
"isSubdomain": false
}

Subdomains get a single A record named after the subdomain part. Create the returned records at your DNS provider, then call the connection check endpoint.

Response Codes

201
Created

Domain added in pending status. Returns DNS record instructions.

400
Bad Request

invalid_domain, origin_host_same_as_domain, or origin_host_is_editor_url.

403
Forbidden

domain_suspended, or domain_limit_reached when your plan's domain limit is used up — the API never purchases extra domain slots; add slots from the dashboard or upgrade your plan.

409
Conflict

domain_already_connected. The domain is already connected to an account.

500
Server Error

insert_failed. The domain could not be saved; retry the request.

503
Service Unavailable

service_unavailable. Configuration could not be applied; retry the request.

Example

javascript
CopyDownload
const response = await fetch("https://encited.com/api/domains", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-lovablehtml-api-key": "<API_KEY>",
},
body: JSON.stringify({
domain: "your-app.com",
originHost: "my-app.lovable.app",
}),
});
const result = await response.json();
console.log(result.records);
bash
CopyDownload
curl -X POST \
"https://encited.com/api/domains" \
-H "Content-Type: application/json" \
-H "x-lovablehtml-api-key: <API_KEY>" \
-d '{
"domain": "your-app.com",
"originHost": "my-app.lovable.app"
}'

Get a Domain

GET/api/domains/:domain

Get the full configuration of a single domain, including settings and sitemap state.

Response Body

json
CopyDownload
{
"id": "a1b2c3d4-e5f6-...",
"domain": "your-app.com",
"status": "connected",
"originHost": "my-app.lovable.app",
"ignorePaths": "/admin/*",
"preferredHost": "apex",
"redirectRules": [
{
"source": "/old-page",
"destination": "/new-page",
"status": 301,
"preserveQuery": true
}
],
"customResponseHeaders": [
{
"pathPattern": "/docs/*",
"headers": [{ "name": "X-Robots-Tag", "value": "noarchive" }]
}
],
"onDemandRenderEnabled": true,
"sitemapUrl": "https://your-app.com/sitemap.xml",
"discoveredUrlCount": 42,
"createdAt": "2026-01-15T09:30:00.000Z",
"updatedAt": "2026-02-01T12:00:00.000Z"
}

Response Codes

200
Success

Returns the domain detail object.

403
Forbidden

api_key_domain_scope_mismatch or domain_not_owned. The key is scoped to a different domain.

404
Not Found

not_found. The domain is not connected to your account.

Example

bash
CopyDownload
curl -X GET \
"https://encited.com/api/domains/your-app.com" \
-H "x-lovablehtml-api-key: <API_KEY>"

Remove a Domain

DELETE/api/domains/:domain

Disconnect a domain and remove its configuration. Requires an account-wide key.

Response Body

json
CopyDownload
{
"deleted": true
}

Response Codes

200
Success

Domain removed.

403
Forbidden

requires_unscoped_key. Domain-scoped keys cannot remove domains.

404
Not Found

not_found. The domain is not connected to your account.

503
Service Unavailable

service_unavailable. Configuration could not be applied; retry the request.

Example

bash
CopyDownload
curl -X DELETE \
"https://encited.com/api/domains/your-app.com" \
-H "x-lovablehtml-api-key: <API_KEY>"

Check Connection

POST/api/domains/:domain/check

Run a DNS lookup and a rendering probe against the domain, and promote it to connected when either succeeds.

The check is promote-only: a pending domain becomes connected when its DNS records match, or via api when the rendering probe succeeds. A failed check never demotes an already-connected domain — a transient DNS failure on your side won't disconnect a live site.

Response Body

json
CopyDownload
{
"dns": {
"match": true,
"cloudflareProxy": false,
"lookupFailed": false
},
"probe": { "ok": true },
"status": "connected",
"promoted": true
}
Parameter Type Description
dns.match boolean DNS records point at Encited with no conflicting records.
dns.cloudflareProxy boolean All resolved IPs belong to Cloudflare — usually means the orange-cloud proxy is hiding your A records.
dns.lookupFailed boolean The DNS lookup itself failed (transient resolver error).
probe.ok boolean A crawler-style request through the domain reached Encited.
status string Stored status after the check, post-promotion when it happened.
promoted boolean True when this check moved the domain out of pending.

Response Codes

200
Success

Check completed. Inspect dns, probe, and promoted.

403
Forbidden

api_key_domain_scope_mismatch or domain_not_owned.

404
Not Found

not_found. The domain is not connected to your account.

429
Rate Limited

rate_limited. Connection checks are limited to ~6 per minute per key.

Example

bash
CopyDownload
curl -X POST \
"https://encited.com/api/domains/your-app.com/check" \
-H "x-lovablehtml-api-key: <API_KEY>"

Update Settings

PATCH/api/domains/:domain/settings

Partially update a domain's settings. Omitted fields are left untouched.

Request Body

All fields are optional; send only what you want to change.

Parameter Type Description
originHost string Host where your SPA is published. Must differ from the domain and must not be an editor URL (400 otherwise, same as adding a domain). Reachability is a soft check: an unreachable value is flagged in invalidFields but still saved and synced. A value that normalizes to an empty host returns 400 invalid_origin_host.
ignorePaths string Path patterns to exclude from prerendering, one per line. Send an empty string to clear.
preferredHost "apex" | "www" | null Primary host to canonicalize to. Send null to clear the preference.
redirectRules array Full replacement list of { source, destination, status?, preserveQuery? } rules. status is 301 (default) or 307; preserveQuery defaults to true.
customResponseHeaders array Full replacement list of { pathPattern, headers } rules (max 10 rules, 5 headers each). Plan-gated.
onDemandRenderEnabled boolean Enable or disable on-demand rendering for the domain.

Response Body

Returns the updated domain detail object (same shape as Get a Domain) plus invalidFields. The update is applied per field: valid fields are saved even when others fail validation, and every failing field name is listed in invalidFields. Entries can be per-rule keys like redirectRules.0.source, not just top-level field names. A field listed in invalidFields is not necessarily discarded — an originHost that fails the reachability check is still saved and synced to the proxy config, so verify the stored value with a follow-up Get a Domain request.

json
CopyDownload
{
"id": "a1b2c3d4-e5f6-...",
"domain": "your-app.com",
"status": "connected",
"originHost": "my-app.lovable.app",
"ignorePaths": "/admin/*",
"preferredHost": "apex",
"redirectRules": [],
"customResponseHeaders": [],
"onDemandRenderEnabled": true,
"sitemapUrl": "https://your-app.com/sitemap.xml",
"discoveredUrlCount": 42,
"createdAt": "2026-01-15T09:30:00.000Z",
"updatedAt": "2026-02-01T12:00:00.000Z",
"invalidFields": []
}

Check invalidFields even on 200. A response like "invalidFields": ["originHost"] means that field failed a check (e.g., the origin host was unreachable) — but the value may still have been saved and synced. Confirm the stored value with Get a Domain.

Response Codes

200
Success

Settings saved. Rejected fields, if any, are listed in invalidFields.

400
Bad Request

invalid_origin_host (the originHost value does not normalize to a valid host), origin_host_same_as_domain, origin_host_is_editor_url, redirect_limit_reached (includes limit and current), or invalid_custom_headers (includes details).

403
Forbidden

upgrade_required_for_custom_headers, api_key_domain_scope_mismatch, or domain_not_owned.

404
Not Found

not_found. The domain is not connected to your account.

429
Rate Limited

rate_limited. Settings updates count against the ~60/minute limit.

503
Service Unavailable

service_unavailable. Configuration could not be applied; retry the request.

Example

javascript
CopyDownload
const response = await fetch(
"https://encited.com/api/domains/your-app.com/settings",
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
"x-lovablehtml-api-key": "<API_KEY>",
},
body: JSON.stringify({
ignorePaths: "/admin/*",
redirectRules: [
{ source: "/old-page", destination: "/new-page", status: 301 },
],
}),
},
);
const result = await response.json();
console.log(result.invalidFields); // [] when everything applied
bash
CopyDownload
curl -X PATCH \
"https://encited.com/api/domains/your-app.com/settings" \
-H "Content-Type: application/json" \
-H "x-lovablehtml-api-key: <API_KEY>" \
-d '{
"ignorePaths": "/admin/*",
"redirectRules": [
{ "source": "/old-page", "destination": "/new-page", "status": 301 }
]
}'

Update Sitemap

PATCH/api/domains/:domain/sitemap

Set, clear, or refresh the domain's sitemap. Fetches and validates the sitemap, then reports the discovered URL count.

Request Body

The JSON body is optional. An empty request, {}, and { "refresh": true } are all equivalent: they re-fetch and re-validate the currently stored sitemap and re-run URL discovery, so they count against the tight rate limit and can fail with fetch_failed. To read the current state without fetching, use Get a Domain instead.

Parameter Type Description
sitemapUrl string Sitemap URL to set. Sending "" clears the stored sitemap. Omitting the field keeps the existing URL.
refresh boolean Force a re-fetch and re-discovery even when the URL is unchanged. Without it, resubmitting the stored URL is a no-op that returns the cached count.

Response Body

Returns the domain detail object (same shape as Get a Domain). discoveredUrlCount reflects the number of URLs found in the sitemap after the fetch.

json
CopyDownload
{
"id": "a1b2c3d4-e5f6-...",
"domain": "your-app.com",
"status": "connected",
"originHost": "my-app.lovable.app",
"ignorePaths": "",
"preferredHost": null,
"redirectRules": [],
"customResponseHeaders": [],
"onDemandRenderEnabled": true,
"sitemapUrl": "https://your-app.com/sitemap.xml",
"discoveredUrlCount": 42,
"createdAt": "2026-01-15T09:30:00.000Z",
"updatedAt": "2026-02-01T12:00:00.000Z"
}

Response Codes

200
Success

Sitemap saved (or cleared) and the detail object returned.

400
Bad Request

fetch_failed (sitemap URL unreachable) or parse_failed (not valid XML sitemap content).

403
Forbidden

api_key_domain_scope_mismatch or domain_not_owned.

404
Not Found

not_found. The domain is not connected to your account.

429
Rate Limited

rate_limited. Calls that fetch the sitemap count against the ~6/minute limit; only clears ("") and resubmitting the same stored URL without refresh skip the fetch.

503
Service Unavailable

service_unavailable. Configuration could not be applied; retry the request.

Example

bash
CopyDownload
# Set (or change) the sitemap URL
curl -X PATCH \
"https://encited.com/api/domains/your-app.com/sitemap" \
-H "Content-Type: application/json" \
-H "x-lovablehtml-api-key: <API_KEY>" \
-d '{ "sitemapUrl": "https://your-app.com/sitemap.xml" }'
# Force a re-fetch of the stored sitemap
curl -X PATCH \
"https://encited.com/api/domains/your-app.com/sitemap" \
-H "Content-Type: application/json" \
-H "x-lovablehtml-api-key: <API_KEY>" \
-d '{ "refresh": true }'
# Clear the sitemap
curl -X PATCH \
"https://encited.com/api/domains/your-app.com/sitemap" \
-H "Content-Type: application/json" \
-H "x-lovablehtml-api-key: <API_KEY>" \
-d '{ "sitemapUrl": "" }'

Rate Limits

Limits are applied per API key:

Operations Limit
Connection check, sitemap updates that fetch ~6 requests/minute
Everything else ~60 requests/minute

Over the limit, requests receive 429 { "error": "rate_limited" }. Back off and retry after a minute.

Avatar
How can we help?
Get instant answers to your questions or leave a message for an engineer will reach out
Ask AI about Encited
See our docs
Contact support
Leave a message
We'll get back to you soon
Avatar
Ask AI about Encited
Team is also here to help
Thinking
Preview
Drop an image to attach
Powered by ReplyMaven