# API, MCP and agents

Developer documentation

An agent can submit a choice, receive a clarifying question, then get a recommendation with its reasons. No account or provider key is required.

REST v1.1.0 MCP · Streamable HTTP [Markdown ↗](https://dilem.si/en/docs/index.md)

## [MCP ↗](https://dilem.si/en/docs/#connect)

For assistants and MCP clients. Configure the server, then discover its tools.

## [REST API ↗](https://dilem.si/en/docs/#quickstart)

For your application or script. HTTP, Bearer authentication and JSON responses.

## [WebMCP ↗](https://dilem.si/en/docs/#browser)

For an agent in the open page. Uses browser identity and history.

## Connect with MCP

MCP address

```
https://dilem.si/api/v1/mcp
```

Remote HTTP transport, without OAuth. Pass the application token to tools, not in the server configuration. Allow 110 seconds for a decision call.

Choose your client. Merge the example into your existing configuration rather than replacing it. Reload MCP tools, then check that start_session, get_usage and decide are available.

**Claude Code**

Terminal · Bash

```
claude mcp add --transport http dilem https://dilem.si/api/v1/mcp
```

[Client documentation ↗](https://code.claude.com/docs/en/mcp)

**Codex**

Configuration file

```
~/.codex/config.toml
```

Configuration · TOML

```
[mcp_servers.dilem]
url = "https://dilem.si/api/v1/mcp"
tool_timeout_sec = 110
```

[Client documentation ↗](https://developers.openai.com/codex/mcp/)

**Cursor**

Configuration file

```
.cursor/mcp.json
```

Configuration · JSON

```
{
  "mcpServers": {
    "dilem": {
      "url": "https://dilem.si/api/v1/mcp"
    }
  }
}
```

[Client documentation ↗](https://cursor.com/docs/mcp)

**OpenCode**

Configuration file

```
opencode.json
```

Configuration · JSON

```
{
  "mcp": {
    "dilem": {
      "type": "remote",
      "url": "https://dilem.si/api/v1/mcp",
      "enabled": true,
      "oauth": false
    }
  }
}
```

[Client documentation ↗](https://opencode.ai/docs/mcp-servers/)

**Hermes Agent**

Terminal · Bash

```
hermes mcp add dilem --url https://dilem.si/api/v1/mcp
```

[Client documentation ↗](https://hermes-agent.nousresearch.com/docs/user-guide/features/mcp)

**Mistral Vibe**

Configuration file

```
~/.vibe/config.toml
```

Configuration · TOML

```
[[mcp_servers]]
name = "dilem"
transport = "streamable-http"
url = "https://dilem.si/api/v1/mcp"
tool_timeout_sec = 110

[mcp_servers.auth]
type = "static"
headers = {}
```

[Client documentation ↗](https://docs.mistral.ai/vibe/code/cli/mcp-servers)

**OpenClaw**

Configuration file

```
~/.openclaw/openclaw.json
```

Configuration · JSON

```
{
  "mcp": {
    "servers": {
      "dilem": {
        "url": "https://dilem.si/api/v1/mcp",
        "transport": "streamable-http",
        "enabled": true,
        "connectionTimeoutMs": 10000,
        "requestTimeoutMs": 110000
      }
    }
  }
}
```

[Client documentation ↗](https://docs.openclaw.ai/tools/mcp)

**Pi**

Configuration file

```
~/.pi/agent/mcp.json
```

Configuration · JSON

```
{
  "mcpServers": {
    "dilem": {
      "url": "https://dilem.si/api/v1/mcp",
      "timeout": 110
    }
  }
}
```

[Client documentation ↗](https://pi.dev/docs/latest/mcp)

## MCP reference

### [start_session](https://dilem.si/en/docs/#tool-start_session)

Write

Create or renew the client’s private identity.

- `session` `string | null` optional · Default null

Response: `Session`

**POST `/api/v1/session`**

#### Input

##### Headers

- `Authorization` `string`

Session token sent as Bearer <session>.

No request body required.

#### Response

##### 200 `application/json`

Illustrative example · JSON

```
{
  "session": "OPAQUE_SESSION_KEEP_PRIVATE",
  "expires_in": 7776000
}
```

- `session` `string` · required

Private client token to retain for subsequent calls.

- `expires_in` `integer` · required

Session validity period, in seconds.

Value: `7776000`

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

### [get_usage](https://dilem.si/en/docs/#tool-get_usage)

Read

Read credits, reset time and optional case state.

- `session` `string` required
- `case_id` `string | null` optional · Default null

Response: `Usage`

**GET `/api/v1/usage`**

#### Input

##### Headers

- `Authorization` `string` · required

Session token sent as Bearer <session>.

- `X-Dilem-Case-ID` `string`

Keep this identifier for every turn of the same dilemma.

**Technical format**

`^[a-zA-Z0-9_-]{1,80}$`

No request body required.

#### Response

##### 200 `application/json`

Illustrative example · JSON

```
{
  "day": "2026-01-01",
  "resets_at": "2026-01-02T00:00:00Z",
  "collective": {
    "completed": 42
  },
  "dilemmas": {
    "used": 1,
    "limit": 20,
    "remaining": 19
  },
  "case": {
    "id": "my-unique-dilemma-id",
    "admitted": true,
    "completed": false
  },
  "voice": {
    "seconds_used": 0,
    "seconds_reserved": 0,
    "seconds_remaining": 1200,
    "seconds_limit": 1200,
    "max_seconds": 60
  },
  "budget": {
    "daily_usd": 5,
    "available": true
  }
}
```

- `day` `string` · required

UTC day of the counters.

ISO 8601 date

- `resets_at` `string` · required

Date and time of the next credit reset.

ISO 8601 date and time

- `collective` `object` · required

Shared count of completed dilemmas.

**Fields**

- `completed` `integer` · required

Number of completed dilemmas in the shared counter.

- `dilemmas` `object` · required

Used, remaining and allowed dilemma credits.

**Fields**

- `used` `integer` · required

Credits already used.

Minimum: 0

- `limit` `integer` · required

Credit limit from the active policy.

Minimum: 0

- `remaining` `integer` · required

Credits still available.

Minimum: 0

- `case` `object` · required

State of the dilemma identified by the supplied Case-ID.

**Fields**

- `id` `string | null` · required

Requested Case-ID, or null when none was supplied.

- `admitted` `boolean` · required

Whether this dilemma has already been admitted under the quota.

- `completed` `boolean` · required

Whether a first recommendation completed this dilemma.

- `voice` `object` · required

Dictation durations and limits, in seconds.

**Fields**

- `seconds_used` `number`

Dictation seconds already used.

- `seconds_reserved` `number`

Seconds temporarily reserved by an active capture.

- `seconds_remaining` `number`

Dictation seconds still available.

- `seconds_limit` `number`

Total dictation limit, in seconds.

- `max_seconds` `number`

Maximum capture duration, in seconds.

- `budget` `object` · required

Shared financial budget and service availability.

**Fields**

- `daily_usd` `number` · required

Daily service budget limit, in US dollars.

- `available` `boolean` · required

Whether the budget still allows calculations.

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

### [decide](https://dilem.si/en/docs/#tool-decide)

AI calculation

Submit a decision turn: question, recommendation or error.

- `session` `string` required
- `case_id` `string` required
- `text` `string` required
- `language` `"fr" | "en" | "it" | "de" | "es" | "pt"` optional · Default "fr"
- `refinements` `string[] | null` optional · Default null
- `force_decision` `boolean` optional · Default false
- `clarification_count` `integer` optional · Default 0
- `clarification_version` `2` optional · Default 2

Response: `DecisionResponse`

**POST `/api/v1/decide`**

#### Input

##### Headers

- `Authorization` `string` · required

Session token sent as Bearer <session>.

- `X-Dilem-Case-ID` `string` · required

Keep this identifier for every turn of the same dilemma.

**Technical format**

`^[a-zA-Z0-9_-]{1,80}$`

##### Body `application/json`

REST · Request · Illustrative example · JSON

```
{
  "text": "Train or bus for my commute?",
  "language": "en",
  "refinements": [],
  "clarification_version": 2
}
```

- `text` `string` · required

Initial choice, retained on every turn.

1–6 000 characters

- `language` `string` · required

Interface language. Replies follow the user’s own words.

Allowed values: `"fr"`, `"en"`, `"it"`, `"de"`, `"es"`, `"pt"`

- `refinements` `string[]`

Actual questions and answers in chronological order.

Up to 40 items · Up to 6 000 characters per item · Default: `[]`

- `force_decision` `boolean`

Only when the user requests a recommendation without further questions.

Default: `false`

- `clarification_count` `integer`

Number of clarification answers.

0–20 · Default: `0`

- `clarification_version` `integer`

Contract version, always 2.

Default: `2` · Value: `2`

MCP · decide · Request · JSON

```
{
  "session": "<session from start_session>",
  "case_id": "my-unique-dilemma-id",
  "text": "Train or car for a weekend trip?",
  "language": "en"
}
```

#### Response

##### 200 `application/json`

**Clarifying question `Clarification`**

A question to relay, or a notice according to purpose. insufficient_evidence means there is no defensible basis for ranking; do not retry without new information from the user.

Illustrative example · JSON

```
{
  "type": "clarification",
  "question": "How far is your commute?",
  "choices": [],
  "format": "number",
  "unit": "km",
  "summary": "Train or bus"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"clarification"`

- `question` `string` · required

Question to relay to the user before continuing.

- `choices` `string[]` · required

Suggested answers, when supplied with the question.

Up to 6 items

- `format` `string` · required

Answer format expected by the question.

Allowed values: `"single"`, `"multiple"`, `"text"`, `"number"`

- `unit` `string | null`

Unit of a numeric answer, or null.

- `summary` `string` · required

Summary of the dilemma.

- `purpose` `string`

Purpose of the question or scope notice.

Allowed values: `"decision_scope"`, `"scope_notice"`, `"insufficient_evidence"`

- `metrics` `object`

Technical processing metrics.

**Recommendation `Result`**

Recommendation, options, reasons and sources. Weights express relative preferences, not success probabilities. result_id may be absent.

Illustrative example · JSON

```
{
  "type": "result",
  "summary": "Train or bus for my commute?",
  "options": [
    {
      "id": "train",
      "label": "Take the train",
      "description": "Use the direct rail connection.",
      "probability": 0.7
    },
    {
      "id": "bus",
      "label": "Take the bus",
      "description": "Use the bus stop near home.",
      "probability": 0.3
    }
  ],
  "selected": "train",
  "sources": [],
  "result_id": "0123456789abcdef0123456789abcdef"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"result"`

- `summary` `string` · required

Summary of the dilemma.

- `context` `string`

Context used for the recommendation.

- `assumptions` `string[]`

Assumptions used to compare options.

- `criteria` `string[]`

Criteria used to compare options.

- `options` `object[]` · required

Compared options and associated information.

2–8 items

- `selected` `string` · required

Identifier of the recommended option.

- `sources` `object[]` · required

Sources returned with the recommendation.

- `caution` `string | null`

Specific caution, or null.

- `timings` `object`

Durations of processing stages.

- `models` `object`

Model information for this turn.

- `metrics` `object`

Technical processing metrics.

- `result_id` `string`

Identifier of a result owned by this client.

**Error `Error`**

Pipeline refusal or failure. Check type even after HTTP 200; MCP also reports isError.

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

### [export_result_card](https://dilem.si/en/docs/#tool-export_result_card)

Read

Render the received result as a card, without AI or image storage.

- `session` `string` required
- `result` `CardResult` required
- `language` `"fr" | "en" | "it" | "de" | "es" | "pt"` optional · Default "fr"
- `theme` `"light" | "dark"` optional · Default "light"

Response: `image/png`

**POST `/api/v1/result-card`**

#### Input

##### Headers

- `Authorization` `string` · required

Session token sent as Bearer <session>.

##### Body `application/json`

REST · Request · Illustrative example · JSON

```
{
  "result": {
    "summary": "Train or bus for my commute?",
    "selected": "train",
    "options": [
      {
        "id": "train",
        "label": "Take the train",
        "description": "Use the direct rail connection.",
        "probability": 0.7
      },
      {
        "id": "bus",
        "label": "Take the bus",
        "description": "Use the bus stop near home.",
        "probability": 0.3
      }
    ]
  },
  "language": "en",
  "theme": "light"
}
```

- `result` `CardResult` · required

Previously received result to supply for PNG rendering.

**Fields**

- `summary` `string` · required

Summary of the dilemma.

1–6 000 characters

- `selected` `string` · required

Identifier of the recommended option.

Up to 32 characters

- `options` `CardOption[]` · required

Compared options and associated information.

2–8 items

**Fields**

- `id` `string` · required

Unique identifier of this option within the result.

**Technical format**

`^[a-z][a-z0-9_]{0,31}$`

- `label` `string` · required

Option label on the card.

1–120 characters

- `description` `string`

Short description shown with the option.

Up to 240 characters · Default: `""`

- `probability` `number` · required

Preference weight, not a probability of success.

0–1

- `language` `string`

Presentation language.

Allowed values: `"fr"`, `"en"`, `"it"`, `"de"`, `"es"`, `"pt"` · Default: `"fr"`

- `theme` `string`

Visual theme of the PNG card.

Allowed values: `"light"`, `"dark"` · Default: `"light"`

#### Response

##### 200 `image/png`

Private PNG attachment. Its filename is returned in Content-Disposition.

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 · 415 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

### [get_result_feedback](https://dilem.si/en/docs/#tool-get_result_feedback)

Read

Read the owning client’s result vote.

- `session` `string` required
- `result_id` `string` required

Response: `Feedback`

**GET `/api/v1/feedback/{result_id}`**

#### Input

##### Headers

- `Authorization` `string` · required

Session token sent as Bearer <session>.

##### Path parameters

- `result_id` `string` · required

Identifier of a result owned by this client.

**Technical format**

`^[0-9a-f]{32}$`

No request body required.

#### Response

##### 200 `application/json`

Illustrative example · JSON

```
{
  "result_id": "0123456789abcdef0123456789abcdef",
  "helpful": false,
  "reason": "missing_constraint"
}
```

- `result_id` `string` · required

Identifier of a result owned by this client.

**Technical format**

`^[0-9a-f]{32}$`

- `helpful` `boolean | null` · required

Recorded vote, or null when no vote was given.

- `reason` `null | string` · required

Reason for a negative vote, or null.

Allowed values: `null`, `"misunderstood"`, `"missing_constraint"`, `"too_many_questions"`, `"too_vague"`

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 · 404 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

### [set_result_feedback](https://dilem.si/en/docs/#tool-set_result_feedback)

Write

Record the user’s explicit vote; never infer satisfaction.

- `session` `string` required
- `result_id` `string` required
- `helpful` `boolean` required
- `reason` `"misunderstood" | "missing_constraint" | "too_many_questions" | "too_vague" | null` optional · Default null

Response: `Feedback`

**POST `/api/v1/feedback`**

#### Input

##### Headers

- `Authorization` `string` · required

Session token sent as Bearer <session>.

##### Body `application/json`

REST · Request · Illustrative example · JSON

```
{
  "result_id": "0123456789abcdef0123456789abcdef",
  "helpful": false,
  "reason": "missing_constraint"
}
```

- `result_id` `string` · required

Identifier of a result owned by this client.

**Technical format**

`^[0-9a-f]{32}$`

- `helpful` `boolean` · required

User’s explicit vote on the response.

- `reason` `null | string`

Reason for a negative vote, or null.

Allowed values: `null`, `"misunderstood"`, `"missing_constraint"`, `"too_many_questions"`, `"too_vague"`

#### Response

##### 200 `application/json`

Illustrative example · JSON

```
{
  "result_id": "0123456789abcdef0123456789abcdef",
  "helpful": false,
  "reason": "missing_constraint"
}
```

- `result_id` `string` · required

Identifier of a result owned by this client.

**Technical format**

`^[0-9a-f]{32}$`

- `helpful` `boolean | null` · required

Recorded vote, or null when no vote was given.

- `reason` `null | string` · required

Reason for a negative vote, or null.

Allowed values: `null`, `"misunderstood"`, `"missing_constraint"`, `"too_many_questions"`, `"too_vague"`

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 · 415 · 404 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

### [send_user_feedback](https://dilem.si/en/docs/#tool-send_user_feedback)

Write

Send an explicitly authorized message or report.

- `session` `string` required
- `request_id` `string` required
- `message` `string` required
- `language` `"fr" | "en" | "it" | "de" | "es" | "pt"` optional · Default "fr"
- `kind` `"general" | "response"` optional · Default "general"
- `email` `string` optional · Default ""
- `response` `object | null` optional · Default null

Response: `FeedbackAck`

**POST `/api/v1/user-feedback`**

#### Input

##### Headers

- `Authorization` `string` · required

Session token sent as Bearer <session>.

##### Body `application/json`

REST · Request · Illustrative example · JSON

```
{
  "request_id": "0123456789abcdef0123456789abcdef",
  "kind": "general",
  "message": "Please explain how the recommendation uses my constraints.",
  "email": "",
  "language": "en",
  "source": "agent",
  "version": "unknown",
  "screen": "other",
  "response": null
}
```

- `request_id` `string` · required

Identifier to retain for deduplicating the same submission.

**Technical format**

`^[0-9a-f]{32}$`

- `kind` `string` · required

General message or report of a response.

Allowed values: `"general"`, `"response"`

- `message` `string` · required

Comment expressly provided by the user.

5–4 000 characters

- `email` `string` · required

Contact address, or an empty string.

Up to 254 characters

- `language` `string` · required

Presentation language.

Allowed values: `"fr"`, `"en"`, `"it"`, `"de"`, `"es"`, `"pt"`

- `source` `string` · required

Entry point from which the message originated.

Allowed values: `"agent"`, `"menu"`, `"help"`, `"response"`

- `version` `string` · required

Application SHA, or unknown.

**Technical format**

`^[0-9a-f]{40}$|^unknown$`

- `screen` `string` · required

Screen from which the message is sent.

Allowed values: `"home"`, `"clarification"`, `"result"`, `"other"`

- `response` `null | ReportedResponse` · required

Exact response attached to the report, otherwise null.

**Fields**

**`null`**

`null`

**Attached response `ReportedResponse`**

- `kind` `string` · required

Kind of response attached to the report.

Allowed values: `"clarification"`, `"recommendation"`

- `text` `string` · required

Exact text of the attached response.

1–12 000 characters

- `reference` `string` · required

Reference of the response concerned.

**Technical format**

`^[A-Za-z0-9._:-]{1,80}$`

#### Response

##### 200 `application/json`

Illustrative example · JSON

```
{
  "ok": true
}
```

- `ok` `boolean` · required

Confirms the message was recorded.

Value: `true`

**Errors and recovery**

403 · 408 · 409 · 413 · 422 · 429 · 503 · 415 `application/json`

Illustrative example · JSON

```
{
  "type": "error",
  "code": "case_completed"
}
```

- `type` `string` · required

Kind of response returned.

Value: `"error"`

- `code` `string` · required

Code to inspect when handling a refusal or failure.

- `message` `string`

Readable explanation of the refusal or failure.

- `retryable` `boolean`

Whether another attempt may be considered.

- `resets_at` `string`

Date and time of the next credit reset.

ISO 8601 date and time

## Quickstart

Requires curl and Python 3. Create an identity once, store its token privately and restore it on later visits.

Create a session and save the session value. Send it as Authorization: Bearer *** on subsequent calls. It lasts 90 days; renew it with the same call and token.

POST /api/v1/session · Bash

```
DILEM_SESSION=$(curl --fail-with-body --silent --show-error --max-time 15 \
  https://dilem.si/api/v1/session \
  -H 'Content-Type: application/json' -d '{}' | \
  python3 -c 'import json, sys; print(json.load(sys.stdin)["session"])')
```

GET /api/v1/usage · Bash

```
curl --fail-with-body --max-time 15 https://dilem.si/api/v1/usage \
  -H "Authorization: Bearer ${DILEM_SESSION:?}" \
  -H 'X-Dilem-Case-ID: my-unique-dilemma-id'
```

Choose a Case-ID for your dilemma and keep it throughout the conversation. You receive a question or a recommendation. To answer, append the question and your answer to refinements, then increment clarification_count by 1.

POST /api/v1/decide · Bash

```
curl --fail-with-body --max-time 110 --output result.json https://dilem.si/api/v1/decide \
  -H "Authorization: Bearer ${DILEM_SESSION:?}" \
  -H 'X-Dilem-Case-ID: my-unique-dilemma-id' \
  -H 'Content-Type: application/json' \
  -d '{"text":"Train or car for a weekend trip?","language":"en","refinements":[],"clarification_count":0,"clarification_version":2}'
```

## Session and authentication

Without a token, start_session creates an identity. With session, it renews the same identity for 90 days. Invalid or expired tokens are refused, never silently replaced. REST and MCP share this token; neither reads browser cookies.

Use one unique Case-ID per dilemma until its result: 1–80 characters from a-z, A-Z, 0-9, _ and -. It identifies the case, but does not make a calculation idempotent.

## Dilemma lifecycle

1. Submit the initial choice with a new Case-ID.
2. On clarification, inspect purpose: insufficient_evidence is a notice to show, not a question or invitation to invent a ranking. Otherwise ask the question and wait for the actual answer; append question + answer to refinements and increment clarification_count.
3. On result, retain the JSON: this case is complete. Use a new Case-ID for another calculation.
4. On error, inspect code and retryable. Never automatically replay a calculation after an ambiguous outcome.

The agent passes questions to you and waits for your answers, without inventing missing information. You can ask it for a recommendation without further questions.

## Decision parameters

- `text` required

Initial choice, retained on every turn.

`string · 1–6000`
- `language` required

Interface language. Replies follow the user’s own words.

`fr | en | it | de | es | pt`
- `refinements` optional

Actual questions and answers in chronological order.

`array · ≤40 × 6000`
- `clarification_count` optional

Number of clarification answers.

`integer · 0–20`
- `clarification_version` optional

Contract version, always 2.

`2`
- `force_decision` optional

Only when the user requests a recommendation without further questions.

`boolean`

Text + refinements: 24,000 characters total. HTTP body: 300,000 bytes received within 10 seconds; an oversized provider envelope can also be refused before consumption.

## Responses

### Clarifying question `type: clarification`

A question to relay, or a notice according to purpose. insufficient_evidence means there is no defensible basis for ranking; do not retry without new information from the user.

`type, question, choices, format, unit, summary, purpose, metrics`

### Recommendation `type: result`

Recommendation, options, reasons and sources. Weights express relative preferences, not success probabilities. result_id may be absent.

`type, summary, context, assumptions, criteria, options, selected, sources, caution, timings, models, metrics, result_id`

### Error `type: error`

Pipeline refusal or failure. Check type even after HTTP 200; MCP also reports isError.

`type, code, message, retryable, resets_at`

Illustrative example · clarification · JSON

```
{
  "type": "clarification",
  "question": "How far is your commute?",
  "choices": [],
  "format": "number",
  "unit": "km",
  "summary": "Train or bus"
}
```

## Results and feedback

### Export a result as PNG

Pass the received result as result, with language and theme (light or dark). Private PNG, filename in Content-Disposition, Cache-Control: no-store. No dilemma credit and no publication. MCP returns a native image and file metadata.

Request · Illustrative example · Bash

```
python3 -c 'import json, sys; print(json.dumps({"result": json.load(sys.stdin), "language": "fr", "theme": "light"}))' < result.json > card-input.json
```

POST /api/v1/result-card · Bash

```
curl --fail-with-body --max-time 15 https://dilem.si/api/v1/result-card \
  -H "Authorization: Bearer ${DILEM_SESSION:?}" \
  -H "Content-Type: application/json" \
  --data-binary @card-input.json --output dilem.png
```

### Read or record a vote

result_id belongs to this identity. helpful is a boolean; reason is optional for a negative vote only: misunderstood, missing_constraint, too_many_questions or too_vague. Missing, expired and foreign results return the same 404.

`GET /api/v1/feedback/{result_id}`

Request · Illustrative example · JSON

```
{"result_id": "<result_id from your result>", "helpful": false, "reason": "missing_constraint"}
```

POST /api/v1/feedback · Bash

```
curl --fail-with-body --max-time 15 https://dilem.si/api/v1/feedback \
  -H "Authorization: Bearer ${DILEM_SESSION:?}" \
  -H "Content-Type: application/json" \
  --data-binary @feedback-input.json
```

### Send a message or report a response

request_id: 32 lowercase hexadecimal characters. message: at least 5 non-whitespace characters, 4,000 maximum. kind=general for a message; kind=response for a report with the exact response attached. Email is optional. Keep identifier and payload stable; no automatic replay after ambiguous failure.

Request · Illustrative example · JSON

```
{
  "request_id": "<32 lowercase hex characters>",
  "kind": "general",
  "message": "<user-authorized message>",
  "email": "",
  "language": "fr",
  "source": "agent",
  "version": "unknown",
  "screen": "other",
  "response": null
}
```

POST /api/v1/user-feedback · Bash

```
curl --fail-with-body --max-time 15 https://dilem.si/api/v1/user-feedback \
  -H "Authorization: Bearer ${DILEM_SESSION:?}" \
  -H "Content-Type: application/json" \
  --data-binary @user-feedback-input.json
```

## Credits

One credit covers a dilemma through its first recommendation, including clarification questions. Any new calculation after that recommendation uses another credit.

Use get_usage over MCP or GET /api/v1/usage over REST to check your remaining credits and when they renew.

Limits come from the active policy, not a fixed value in this guide. PNG export and feedback use no dilemma credit, but retain technical and anti-abuse limits.

## Errors and recovery

[`HTTP 403`](https://dilem.si/openapi.json)[`HTTP 408`](https://dilem.si/openapi.json)[`HTTP 409`](https://dilem.si/openapi.json)[`HTTP 413`](https://dilem.si/openapi.json)[`HTTP 422`](https://dilem.si/openapi.json)[`HTTP 429`](https://dilem.si/openapi.json)[`HTTP 503`](https://dilem.si/openapi.json)

403: missing, invalid or expired session, rejected origin or forbidden cookies. 408/413/422: timeout, size or invalid input. 409: completed case. 429: quota, budget or capacity. 503: unavailable service. Also check the response type: pipeline errors can occur after admission. Never automatically replay a calculation after an ambiguous failure; Case-ID is not a calculation idempotency key.

A disconnect may leave consumed work. Reading usage cannot reconstruct a lost response.

## Browser agents

In a compatible browser, WebMCP lets your agent use the open page with your history and credits. Otherwise, use remote MCP or the API. Remote access accepts text; voice dictation remains available in the app.

Read get_dilemma first. submit_dilemma sends a choice or answer; recommend_dilemma requests a recommendation without another question. Result actions use the visible case, not the remote token.

WebMCP adds local image export with preview, votes and reports, plus list_saved_dilemmas and get_saved_dilemma for this browser’s history. The image URL expires when the preview closes. Neither that history nor browser cookies are shared with remote MCP.

## Technical reference

OpenAPI schemas describe complete request and response bodies. The Markdown guide is an agent entry point; the catalog and Server Card expose MCP discovery.

[OpenAPI 3.1](https://dilem.si/openapi.json)[AI Catalog](https://dilem.si/.well-known/ai-catalog.json)[MCP Server Card](https://dilem.si/mcp-server-card.json)[Agent guide · Markdown](https://dilem.si/agents.md)[llms.txt](https://dilem.si/llms.txt)

The service retains text exchanges for 30 days. Browser history stays on your device and is not shared with a remote agent.

[Privacy and providers](https://dilem.si/en/privacy/)

[Contact · contact@dilem.si](mailto:contact@dilem.si)

[HTML](https://dilem.si/en/docs/)
