LiyaEngine Docs
Guides

Custom Domain Walkthrough

End-to-end guide to building and deploying a custom AI domain.

Dashboard

This guide walks through creating a custom domain from scratch — defining the domain, adding intents, uploading knowledge sources, and making your first API call.

We'll build a legal-contracts domain as the example.


Step 1: Create the domain

POST https://api.liyaengine.ai/dashboard/custom-domains
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "domainKey": "legal-contracts",
  "displayName": "Legal Contracts",
  "description": "Contract review, clause extraction, and risk analysis",
  "systemPrompt": "You are a legal assistant specialised in contract analysis. You help users review, summarise, and extract key clauses from commercial contracts. Always cite the relevant clause when making observations. Do not provide legal advice — present findings factually."
}

domainKey must be lowercase letters, numbers, and hyphens only — underscores are rejected. It becomes the path segment in API calls: POST /v1/legal-contracts/{intent}.


Step 2: Define intents

Each intent maps a business action to a prompt template. Variables in {{double_braces}} become input fields.

description (the intent's goal) is required — an intent can't be created without one.

Clause extraction

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 a specific type of clause from a contract with section references and plain-English summaries.",
  "promptTemplate": "Extract all {{clause_type}} clauses from the following contract. For each clause, provide:\n- The exact clause text\n- Section reference\n- Plain English summary\n\nContract:\n{{contract_text}}"
}

Contract summary

POST https://api.liyaengine.ai/dashboard/custom-domains/legal-contracts/intents
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "intentKey": "contract_summary",
  "displayName": "Contract Summary",
  "description": "Summarises a contract's parties, obligations, payment terms, and notable risks.",
  "promptTemplate": "Summarise the following {{contract_type}} contract. Include:\n- Parties involved\n- Key obligations of each party\n- Payment terms\n- Duration and termination conditions\n- Notable risks or unusual clauses\n\nContract:\n{{contract_text}}"
}

Risk analysis

POST https://api.liyaengine.ai/dashboard/custom-domains/legal-contracts/intents
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "intentKey": "risk_analysis",
  "displayName": "Risk Analysis",
  "description": "Flags high-risk clauses, missing protections, and one-sided terms from a given party's perspective.",
  "promptTemplate": "Analyse the following contract for potential risks from the perspective of {{party_role}}.\n\nIdentify:\n- High-risk clauses\n- Missing standard protections\n- Unusual or one-sided terms\n- Recommended amendments\n\nContract:\n{{contract_text}}"
}

Step 3: Upload knowledge sources (optional)

If you have a library of standard clauses, precedent contracts, or clause definitions, upload them as knowledge sources to enable RAG.

First, enable RAG for your account:

PATCH https://api.liyaengine.ai/dashboard/account/config
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "featureConfig": { "enable_rag": true }
}

Then upload sources via the Knowledge dashboard section. Tag them with the legal-contracts domain so they're scoped correctly.


Step 4: Call your custom domain

Your custom intents use the same runtime contract as the sample domains. input.user_id is required, and session_id is a top-level field on the request — not nested under options:

POST https://api.liyaengine.ai/v1/legal-contracts/clause_extraction
Authorization: Bearer liya_prod_...
Content-Type: application/json

{
  "input": {
    "clause_type": "indemnification",
    "contract_text": "INDEMNIFICATION. Each party (as 'Indemnifying Party') shall defend, indemnify, and hold harmless the other party...",
    "user_id": "user_001"
  },
  "session_id": "sess_01HZ..."
}

Response:

{
  "success": true,
  "data": {
    "output": {
      "response": "I found 2 indemnification clauses:\n\n**Clause 1 — Section 8.1**\n> Each party shall defend, indemnify...\n\n**Plain English:** Both parties agree to protect each other from losses arising from their own actions."
    },
    "session_id": "sess_01HZ...",
    "message_id": "msg_01HZ..."
  },
  "metadata": {
    "intent": "clause_extraction",
    "domain": "legal-contracts",
    "model_used": "gpt-4o-mini",
    "tokens_used": 900,
    "cost_usd": 0.00045,
    "latency_ms": 760,
    "cached": false
  }
}

Step 5: Use sessions for multi-step review

Chain intents in a session for a complete review workflow:

# 1. Summary
POST /v1/legal-contracts/contract_summary
{ "input": { "contract_type": "SaaS", "contract_text": "...", "user_id": "user_001" } }
# -> data.session_id: "sess_review_01"

# 2. Extract key clauses (same session — engine has full summary context)
POST /v1/legal-contracts/clause_extraction
{ "input": { "clause_type": "termination", "user_id": "user_001", ... }, "session_id": "sess_review_01" }

# 3. Risk analysis (engine knows what was extracted)
POST /v1/legal-contracts/risk_analysis
{ "input": { "party_role": "vendor", "user_id": "user_001", ... }, "session_id": "sess_review_01" }

Tips

  • Keep system prompts focused — the domain system prompt applies to all intents. Intent-specific instructions go in the promptTemplate.
  • Variable names matter — use descriptive names (contract_text, not text) as they become self-documenting input fields.
  • Test with short contracts first — iterate on prompt quality before processing large documents.
  • Use sessions — the engine builds context across calls, making later intents in a review session smarter.

For full API reference see Custom Domains API.

On this page