The Liggett Group
Integration reference
Integration reference

Wire EvidenceHub into your stack.

Authentication, request signing, the submit-then-poll pattern, idempotency, error codes, and the OpenAPI spec. The signing protocol is open — the toolchain is your call.

Authentication & signing Submit then poll Idempotency Error codes MCP transport OpenAPI spec
Authentication

Customer-controlled keys, signed requests.

Your team uploads an RSA or EC public key in the admin UI. The matching private key never leaves your infrastructure — we never see it, store it, or transmit it. Every request is signed with that private key; we verify the signature against your stored public key.

Canonical signing string

Build a four-line string with the HTTP method, the request path, the timestamp, and the SHA-256 hex digest of the body, separated by literal newline characters. Sign that string with your private key using SHA-256. Send the base64-encoded signature in X-TLG-Signature. The timestamp must be within the last 5 minutes — older timestamps are rejected as replays.

METHOD\nPATH\nTIMESTAMP\nSHA256(BODY)

Required headers

Sample: signing with openssl + curl

This is one example. The protocol is "sign the canonical string with your RSA or EC private key using SHA-256" — use any crypto library or toolchain your team prefers (Node.js crypto, Python cryptography, Go crypto/rsa, Java java.security, .NET System.Security.Cryptography, AWS KMS, Azure Key Vault, etc.). Nothing in this signing flow requires openssl or shell.

# 1. Build the canonical string and sign it
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
BODY_HASH=$(printf '' | openssl dgst -sha256 -hex | awk '{print $2}')
SIG=$(printf 'POST\n/v1/auth/session\n%s\n%s' "$TS" "$BODY_HASH" \
        | openssl dgst -sha256 -sign private.pem | base64 -w0)

# 2. Open a session (returns a bearer token used by all subsequent calls)
curl -X POST "https://api.theliggettgroup.com/v1/auth/session" \
  -H "X-TLG-API-Key: ${API_KEY}" \
  -H "X-TLG-Signature: ${SIG}" \
  -H "X-TLG-Timestamp: ${TS}"
# → 200 { "session_token": "...", "expires_at": "..." }

Bootstrap: how you get your first key

In the admin UI, paste your RSA public key (PEM). We mint a parked key in pending state and return a one-time nonce. You sign the nonce with your private key and submit the signature back. On valid signature, the key is activated and we hand you the API key plus two deterministic test payloads. On invalid signature, the parked key is hard-deleted — no orphan rows that could be claimed without proving possession.

Submit then poll

200 cache hit, 202 cache miss.

POST /v1/claim-checks returns one of two HTTP statuses on submit. The poll endpoint always returns 200 — the work state lives in the response body's status field.

200 OK on submit (cache hit)

The verdict is returned inline. result.cached is true and result.created_at carries the original scoring timestamp. No poll needed.

202 Accepted on submit (cache miss)

Returns job_id and poll_url. Poll the GET endpoint until status transitions from queued / processing to complete.

200 OK on poll

Always 200 while the job exists. status is one of queued, processing, complete, failed. result is present once status is complete.

Response shapes

# Cache hit on submit (HTTP 200)
{
  "job_id": "job-a3b4c5d6e7f80910",
  "job_type": "claim",
  "status": "complete",
  "result": {
    "verdict": "SUPPORTED",
    "score": { "population": 2, "endpoint": 2, "magnitude": 1, "context": 2, "total": 7 },
    "evidence": [{ "source": "DailyMed", "quote": "...", "fetched_at": "2026-05-11T14:23:00Z" }],
    "cached": true,
    "created_at": "2026-05-08T09:14:22Z"
  },
  "charge": 0.05,
  "duration_ms": 0
}

# Cache miss on submit (HTTP 202)
{
  "job_id": "job-a3b4c5d6e7f80910",
  "status": "queued",
  "poll_url": "/v1/claim-checks/job-a3b4c5d6e7f80910"
}

# Poll response (HTTP 200, status transitions over time)
# → { "status": "processing" }
# → { "status": "complete", "result": {...}, "charge": 0.50, "duration_ms": 4280 }

Polling cadence

First poll at 2 seconds, then exponential backoff with a 30-second cap, for up to 10 minutes. Most cache misses complete within 6 to 90 seconds depending on evidence-source latency. There is no per-key poll budget — polls do not consume credits and do not count against rate limits.

Idempotency

One key, one job.

Pass Idempotency-Key on every POST. UUID v7 is the recommended format because it embeds the millisecond timestamp, which keeps your client-side logs sortable.

Error responses

Common error codes.

Every error response is JSON: {"error": "code", "message": "..."}. Build retry logic against the HTTP status — the message is for human operators.

MCP transport

For AI agents.

Connect any MCP-aware AI client (Claude Code, Cursor, custom agent) over Server-Sent Events. The agent calls EvidenceHub the same way it calls any other MCP tool — no REST wrapper code required.

{
  "mcpServers": {
    "evidencehub": {
      "transport": "sse",
      "url": "https://mcp.theliggettgroup.com/sse",
      "headers": {
        "Authorization": "Bearer ${EVIDENCEHUB_MCP_KEY}"
      }
    }
  }
}

# Tools exposed after connect:
#   submit_claim_check       -> returns job_id (or cached result inline)
#   get_claim_check_result   -> poll by job_id
#   submit_reference_check   -> returns job_id (or cached result inline)
#   get_reference_check_result

# MCP key generation: /app/admin/ → "Generate EvidenceHub MCP Key"
# Requires SECURITY_GROUP role + step-up + 2FA.

Test mode. Send X-TLG-Test-Mode: integration on a REST call (or set the equivalent flag on the MCP client) to use deterministic test payloads issued at key activation. Test calls do not consume credits, do not hit production rate limits, and return a canned verdict.

OpenAPI

Spec and SDK generation.

The OpenAPI 3.1 spec is published alongside the API and can be used to generate clients in any language your tooling supports. Contact us for the spec URL until it is published publicly.

Questions on integration?

Tell us about your stack and we'll walk you through the right shape for your use case — HCP-facing reviewer tool, internal MLR workflow, AI agent, or batch backfill.

Talk to integration