Dike

Reason

Grounded legal Q&A with a hallucination guard that verifies every citation.

POST /v1/reason

Requires a Pay As You Go plan — not available on Free.

Retrieves relevant chunks (same pipeline as /v1/search, rerank always on), prompts the model to answer using only that context with [[REF:canonical_id#article_no]] tokens, then verifies every token against the retrieved set before it reaches you. Invalid refs are stripped, not shown, and counted in grounding.dropped_unverified_refs — Dike never fabricates a citation to avoid an empty answer.

flowchart LR
    A[Question] --> B[Retrieve + rerank<br/>same pipeline as /v1/search]
    B --> C[Model answers using<br/>only retrieved context]
    C --> D{Every REF token<br/>in retrieved set?}
    D -->|yes| E[Marker verified<br/>shown as numbered citation]
    D -->|no| F[Stripped, counted in<br/>dropped_unverified_refs]
    E --> G[Response]
    F --> G

Request

{
  "question": "هل يجوز فسخ عقد الإيجار لتأخر المستأجر عن السداد؟",
  "filters": { "jurisdiction": "EG" },
  "top_k": 12,
  "stream": false,
  "language": "ar"
}

filters accepts the same shape as /v1/search. language is "ar" (default) or "en".

Response (non-streaming)

{
  "answer": "...نعم، وفقاً للمادة 157 [1] ...",
  "citations": [
    {
      "marker": 1,
      "canonical_id": "eg-leg-1948-131",
      "article_no": "157",
      "citation_string": "المادة 157 من القانون المدني رقم 131 لسنة 1948",
      "snippet": "...",
      "verified": true
    }
  ],
  "grounding": { "retrieved": 12, "used": 4, "dropped_unverified_refs": 0 },
  "took_ms": 3210
}

If the answer has zero verified citations and isn't the insufficient-grounding sentinel ("لا أجد سنداً قانونياً كافياً" / an equivalent English refusal), the response still returns normally with grounding.used: 0, plus a response header X-Dike-Grounding: weak — treat that header as a signal to show the answer with a caveat, not as an error.

Streaming

stream: true switches the response to text/event-stream:

data: {"token": "..."}
data: {"token": "..."}
event: citations
data: {"citations": [...], "grounding": {...}}

Token events are buffered until each [[REF:...]] token fully closes, so a REF is never split across a chunk boundary — by the time you see [1] in the stream it's already been rewritten from the raw token. The final citations event carries the same citations/grounding shape as the non-streaming response.

Errors

  • 401 unauthorized
  • 403 plan_required — Free plan; /v1/reason needs Pay As You Go
  • 422 validation_error
  • 503 upstream_unavailable