Custom Domains
Create, configure, and manage custom domains via the Dashboard API.
Custom domains are the production path for LiyaEngine tenants. Use them to define your own AI domains with custom intents, agents, workflows, prompt templates, and knowledge sources. All custom domain management is done through the /dashboard/custom-domains endpoints.
For a guided walkthrough see Custom Domain Walkthrough.
GET /dashboard/custom-domains
List all custom domains for your tenant.
GET https://api.liyaengine.ai/dashboard/custom-domains
Authorization: Bearer <jwt>Response:
Each domain row is returned as stored (snake_case fields), together with its full intents and source_types arrays — there is no separate intentCount summary field.
{
"success": true,
"data": {
"domains": [
{
"id": "cdom_01HZ...",
"domain_key": "legal-contracts",
"display_name": "Legal Contracts",
"description": "AI assistant for contract review and clause extraction",
"system_prompt": "You are a legal assistant specialised in contract analysis...",
"is_active": true,
"status": "active",
"intents": [ { "id": "cint_01HZ...", "intent_key": "clause_extraction", "...": "..." } ],
"source_types": [],
"created_at": "2026-01-20T09:00:00Z",
"updated_at": "2026-02-10T14:30:00Z"
}
]
}
}Requires the admin role or higher (creating, updating, and deleting domains all require admin; listing is open to any dashboard role).
POST /dashboard/custom-domains
Create a new custom domain. Requires the admin role or higher.
POST https://api.liyaengine.ai/dashboard/custom-domains
Authorization: Bearer <jwt>
Content-Type: application/json
{
"domainKey": "legal-contracts",
"displayName": "Legal Contracts",
"description": "AI assistant for contract review and clause extraction",
"systemPrompt": "You are a legal assistant specialised in contract analysis. You help users review, summarise, and extract key clauses from contracts."
}Request body:
| Field | Type | Required | Description |
|---|---|---|---|
domainKey | string | Yes | Unique identifier used in API calls — lowercase letters, numbers, hyphens only |
displayName | string | Yes | Human-readable name shown in the dashboard |
description | string | No | Short description of the domain's purpose |
systemPrompt | string | No | Base system prompt applied to all intents in this domain |
domainKey rules:
- Lowercase letters, numbers, and hyphens only (
^[a-z0-9-]+$) — underscores are rejected - Must be unique within your tenant
- Cannot be changed after creation
Response:
{
"success": true,
"data": {
"domain": {
"id": "cdom_01HZ...",
"domain_key": "legal-contracts",
"display_name": "Legal Contracts",
"is_active": true,
"status": "active",
"created_at": "2026-03-15T10:00:00Z"
}
}
}Requires the account's enable_custom_domains feature flag (Growth plan and above) — returns 403 FEATURE_NOT_ENABLED otherwise.
PATCH /dashboard/custom-domains/:domainKey
Update a custom domain's display name, description, or system prompt. Requires the admin role or higher.
PATCH https://api.liyaengine.ai/dashboard/custom-domains/legal-contracts
Authorization: Bearer <jwt>
Content-Type: application/json
{
"displayName": "Legal Contract Review",
"systemPrompt": "Updated system prompt..."
}Response:
{ "success": true, "data": { "updated": 1 } }DELETE /dashboard/custom-domains/:domainKey
Requires the admin role or higher.
Soft-deletes the domain — sets is_active: false so it (and its intents) drop out of the default dashboard/execution view. This does not delete the domain's intents or any associated knowledge sources, and the underlying rows are not removed from the database.
DELETE https://api.liyaengine.ai/dashboard/custom-domains/legal-contracts
Authorization: Bearer <jwt>Intents
GET /dashboard/custom-domains/:domainKey/intents
GET https://api.liyaengine.ai/dashboard/custom-domains/legal-contracts/intents
Authorization: Bearer <jwt>Response:
{
"success": true,
"data": {
"intents": [
{
"id": "cint_01HZ...",
"intent_key": "clause_extraction",
"display_name": "Clause Extraction",
"description": "Extracts key clauses from a contract",
"prompt_template": "Extract all {{clause_type}} clauses from the following contract:\n\n{{contract_text}}",
"output_schema": null,
"is_active": true
}
]
}
}POST /dashboard/custom-domains/:domainKey/intents
Requires the admin role or higher.
POST https://api.liyaengine.ai/dashboard/custom-domains/legal-contracts/intents
Authorization: Bearer <jwt>
Content-Type: application/json
{
"intentKey": "clause_extraction",
"displayName": "Clause Extraction",
"description": "Extracts key clauses from a contract",
"promptTemplate": "Extract all {{clause_type}} clauses from the following contract:\n\n{{contract_text}}",
"outputSchema": {
"clauses": "array of extracted clause objects",
"summary": "brief summary of findings"
}
}| Field | Type | Required | Description |
|---|---|---|---|
intentKey | string | Yes | Used in the API path |
displayName | string | Yes | Human-readable name |
description | string | Yes | The intent's goal — rejected with 400 INVALID_INPUT if blank |
promptTemplate | string | Yes | Prompt with {{variable}} placeholders for input fields |
outputSchema | object | No | Describes expected output structure — used for documentation only |
inputSchema | object | No | JSON Schema the intent's input object is validated against at execution time |
guardrailsConfig | object | No | Per-intent guardrails override |
The response wraps the created row as stored (snake_case fields, same shape as the intents array above) under data.intent.
Variables in promptTemplate (e.g. {{contract_text}}) become required input fields when calling the intent via the v1 API.
Calling a custom domain intent
Once created, custom intents use the same runtime contract as the sample domains:
POST https://api.liyaengine.ai/v1/legal-contracts/clause_extraction
Authorization: Bearer liya_prod_...
Content-Type: application/json
{
"input": {
"clause_type": "termination",
"contract_text": "This Agreement may be terminated by either party..."
},
"options": {
"session_id": "sess_01HZ..."
}
}See Defining Intents for full details on prompt templates and variable mapping.