emas.ai
DecisionCore
by TechnoNet Sdn Bhd
Implementation Guide  ·  REST API  ·  MCP Server

From zero to your first
explanation in under an hour.

DecisionCore exposes a standard REST API and an MCP server. Any system that can make an HTTP POST request can integrate. This guide covers authentication, both API endpoints, Python and curl examples, and the MCP integration pattern.

Quick start

Three steps to your
first explanation.

Get a free API key, send a test POST, verify the response. The full pipeline runs in seconds.

Free tier: 3 full explanation reports per month. No credit card. Same DeepSeek R1 reasoning pipeline as paid tiers — volume limit only. Get your key at /portal.
Step 1 — Get your API key
# Register at /portal and copy your API key.
# Keys are prefixed with "dc-".

export DC_API_KEY="dc-your-key-here"
Step 2 — Send a test explanation (curl)
curl -s -X POST https://decisioncore.emas.ai/explain \
  -H "Authorization: Bearer $DC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "Loan application rejected",
    "subject_description": "Applicant DSR 62%, policy ceiling 60%",
    "sector": "financial",
    "tenant_id": "my-org"
  }'
Step 3 — Response
{
  "audit_id": "ADMT-EXP-8a2f1c3d",
  "decision": "Loan application rejected",
  "explanation": "The loan application was rejected because the applicant's
Debt Service Ratio (DSR) of 62% exceeds the institution's affordability
limit of 60% (Credit Policy §3.1). The applicant may ask for a
human review of this automated decision...",
  "regulations_cited": [
    "Credit Policy §3.1 — Affordability limit (DSR)",
    "Credit Policy §5 — Human review on request"
  ],
  "reasoning_chain": "THOUGHT: DSR 62% exceeds the 60% limit. Matched Credit Policy §3.1...",
  "report_url": "/explain/report/ADMT-EXP-8a2f1c3d",
  "results_used": 1,
  "results_remaining": 2
}
Authentication

Bearer token
authentication.

All API requests require a Bearer token in the Authorization header. Tokens are issued at registration and never expire — rotate them from the dashboard.

Header format
Authorization: Bearer dc-your-api-key-here
Key prefixScopeDeduction
dc-Full API access — /explain, /explain/report1 result per /explain call
401 Unauthorized — invalid or missing key. 402 Payment Required — token balance is zero. Top up at /portal without changing your API key.
Endpoint

POST /explain

Generates a JSON explanation record. Returns the explanation text, regulatory citations, DeepSeek R1 reasoning chain, and a URL for the PDF report. Deducts 1 result (RM5 at Starter).

POST https://decisioncore.emas.ai/explain
FieldTypeRequiredDescription
decisionstringYesThe decision outcome to explain. E.g. "Loan rejected", "Application shortlisted", "Academic probation".
subject_descriptionstringYesContext about the subject and the factors that drove the decision. E.g. "DSR 62%, institution limit 60%". Max 2,000 chars.
sectorstringNoHint for regulatory corpus selection. One of: financial, hr, academic. Defaults to general corpus.
tenant_idstringNoYour organisation identifier — scopes the audit log. Defaults to your account slug.
languagestringNoOutput language for the explanation text. en (default) or ms (Bahasa Malaysia).
Response schema
{
  "audit_id":         "ADMT-EXP-{uuid}",       // unique record ID
  "decision":         "string",                // echoed from request
  "explanation":      "string",                // plain-language explanation
  "regulations_cited": ["string", ...],        // matched provisions
  "reasoning_chain":  "string",                // DeepSeek R1 thought trace
  "report_url":       "/explain/report/{id}", // PDF download URL
  "results_used":      1,
  "results_remaining": integer
}
Endpoint

POST /explain/report

Generates and returns a formal A4 PDF explanation report. Same request body as /explain. Returns application/pdf. Does NOT deduct an additional result — the PDF is included in the /explain call cost.

POST https://decisioncore.emas.ai/explain/report
Download PDF via curl
curl -s -X POST https://decisioncore.emas.ai/explain/report \
  -H "Authorization: Bearer $DC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "Loan application rejected",
    "subject_description": "Applicant DSR 62%, policy ceiling 60%",
    "sector": "financial",
    "tenant_id": "my-org"
  }' \
  --output dc-explanation.pdf
The PDF contains: decision banner, subject context, regulatory citations (with section numbers), DeepSeek R1 reasoning chain, Audit ID, timestamp, and a plain-language notice of how the subject can ask for a review.

You can also retrieve a previously generated PDF by Audit ID using the URL returned in the /explain response:

Retrieve by Audit ID
curl -s https://decisioncore.emas.ai/explain/report/ADMT-EXP-8a2f1c3d \
  -H "Authorization: Bearer $DC_API_KEY" \
  --output ADMT-EXP-8a2f1c3d.pdf
Language example

Python integration.

Using the standard requests library. Compatible with any Python 3.8+ environment.

Python — explanation + PDF download
import requests
import os

DC_API_KEY = os.environ["DC_API_KEY"]
BASE_URL   = "https://decisioncore.emas.ai"

HEADERS = {
    "Authorization": f"Bearer {DC_API_KEY}",
    "Content-Type":  "application/json",
}

def explain(decision: str, subject_description: str,
            sector: str = "financial", tenant_id: str = "my-org") -> dict:
    r = requests.post(
        f"{BASE_URL}/explain",
        json={
            "decision":            decision,
            "subject_description": subject_description,
            "sector":             sector,
            "tenant_id":          tenant_id,
        },
        headers=HEADERS,
        timeout=60,
    )
    r.raise_for_status()
    return r.json()


def download_report(audit_id: str, output_path: str) -> None:
    r = requests.get(
        f"{BASE_URL}/explain/report/{audit_id}",
        headers=HEADERS,
        timeout=60,
    )
    r.raise_for_status()
    with open(output_path, "wb") as f:
        f.write(r.content)


# Example: loan rejection
result = explain(
    decision="Loan application rejected",
    subject_description="DSR 62%, institution limit 60%",
    sector="financial",
)

print(f"Audit ID:    {result['audit_id']}")
print(f"Explanation: {result['explanation'][:200]}...")
print(f"Regulations: {result['regulations_cited']}")

# Download the formal PDF
download_report(result["audit_id"], "dc-explanation.pdf")
print("PDF saved to dc-explanation.pdf")
Language example

Node.js integration.

Using the native fetch API (Node 18+). No additional packages required.

Node.js — explanation + PDF save
const fs = require('fs');

const DC_API_KEY = process.env.DC_API_KEY;
const BASE_URL   = 'https://decisioncore.emas.ai';

async function explain(decision, subjectDescription, sector = 'financial') {
  const res = await fetch(`${BASE_URL}/explain`, {
    method:  'POST',
    headers: {
      'Authorization': `Bearer ${DC_API_KEY}`,
      'Content-Type':  'application/json',
    },
    body: JSON.stringify({ decision, subject_description: subjectDescription, sector }),
  });
  if (!res.ok) throw new Error(`API error ${res.status}: ${await res.text()}`);
  return res.json();
}

async function downloadReport(auditId, outputPath) {
  const res = await fetch(`${BASE_URL}/explain/report/${auditId}`, {
    headers: { 'Authorization': `Bearer ${DC_API_KEY}` },
  });
  if (!res.ok) throw new Error(`Report error ${res.status}`);
  const buffer = Buffer.from(await res.arrayBuffer());
  fs.writeFileSync(outputPath, buffer);
}

// Example usage
explain('Candidate rejected', 'ATS score 58/100, threshold 65', 'hr')
  .then(async result => {
    console.log('Audit ID:', result.audit_id);
    console.log('Regulations:', result.regulations_cited);
    await downloadReport(result.audit_id, 'dc-explanation.pdf');
    console.log('PDF saved.');
  })
  .catch(console.error);
MCP integration

Claude Desktop
MCP server.

DecisionCore ships as an MCP server — natively callable from Claude and any MCP-compatible client. Three tools available: request_explanation, check_consent, log_decision.

claude_desktop_config.json
{
  "mcpServers": {
    "decisioncore": {
      "command": "/path/to/venv/bin/python3",
      "args": ["/path/to/decisioncore/mcp_server.py"],
      "env": {
        "DC_API_KEY":  "dc-your-key-here",
        "DC_BASE_URL": "https://decisioncore.emas.ai"
      }
    }
  }
}
MCP ToolInputsDescription
request_explanationdecision, subject_description, sectorGenerates a JSON explanation record and returns the explanation text + Audit ID. Deducts 1 result.
check_consentuser_id, purpose_id, tenant_idReturns the current consent status for a given user + purpose combination.
log_decisiondecision, subject_id, outcome, tenant_idWrites a decision record to the Sovereign Audit Log without generating an explanation.
On an Enterprise sovereign node, the MCP server runs as a local stdio subprocess — no network exposure — and MCP clients talk to it without data leaving your machine.
FAQ

Why
DecisionCore?

Why not just ask an LLM to explain the decision directly?

ConcernCalling an LLM directlyDecisionCore
Data sovereigntyYour subject data is sent to an external AI cloudInference runs on TechnoNet's own servers in Kuala Lumpur, or on-premises with Enterprise
Audit trailNo record; regenerating produces a different answerAudit ID and record fingerprint per explanation — verifiable
Regulatory formatFree-form text — no citations or rights notice for an auditor or regulator to checkStructured output: regulation citations, section numbers, rights notice
ConsistencyNon-deterministic — same input, different output each timeDeterministic reasoning chain — same input produces the same structured record
PDF reportYou build it yourselfFormal A4 PDF included — Audit ID, timestamp, subject rights, regulatory citations
AI governance readinessYou interpret the rules yourselfCorpus pre-mapped to the AIGE principles, PDPA, FSA, Employment Act and academic regulations; the proposed AI Governance Bill tracked as it develops
What a real /explain response looks like
{
  "audit_id":          "ADMT-EXP-8a2f1c3d",
  "decision":          "Loan application rejected",
  "explanation":       "The application was declined because the applicant's Debt Service Ratio (DSR) of 62% exceeds the institution's affordability limit of 60% (Credit Policy §3.1). At this ratio, more than 60% of monthly income would go to debt repayments. The applicant may ask for a human review of this automated decision.",
  "regulations_cited": [
    "Credit Policy §3.1 — Affordability limit (DSR)",
    "Credit Policy §5 — Human review on request",
    "AIGE principles — Transparency"
  ],
  "reasoning_chain":   "THOUGHT: DSR is 62%. The institution's limit is 60%. Exceeded by 2 percentage points. Credit Policy §3.1 applies. CONCLUSION: reject — affordability limit exceeded.",
  "report_url":        "/explain/report/ADMT-EXP-8a2f1c3d",
  "results_used":      1,
  "results_remaining": 9
}
ADMT-EXP-{uuid} is DecisionCore's Audit ID. Your audit trail references this ID — keep it in your system records alongside the decision.
Error reference

HTTP status
codes.

StatusMeaningResolution
400Bad Request — missing required field or invalid inputCheck that decision and subject_description are present and non-empty.
401Unauthorised — missing or invalid API keyInclude Authorization: Bearer dc-… header. Verify key in dashboard.
402Payment Required — result balance is zeroTop up at /portal. No plan change required.
422Unprocessable Entity — input validation failureCheck field types. sector must be one of: financial, hr, academic.
429Too Many Requests — rate limit exceededRate limit: 60 requests/minute per API key. Add exponential backoff.
500Internal Server Error — reasoning pipeline failureRetry once. If persistent, contact [email protected] with the request body.
503Service Unavailable — inference server starting upFirst cold-start can take 30–60s. Retry after 10 seconds.
Ready to
build?
Free API key in under a minute. 3 full explanations to start. No credit card.
Get your free API key → Ask an integration question →
WhatsApp us