# POST /v1/ask

Odpowiedz na pytanie o działkę po polsku na podstawie płatnych odczytów landfeed.

Operation ID: `ask`.

## Description

Operacja jest wyłączona bez LANDFEED_ASK_PROVIDER i uwierzytelnienia dostawcy. Wtedy zwraca stan niedostępności bez opłaty. Domyślny budżet wynosi 5 kredytów, maksymalny 20. Budżet jest rezerwowany przed pracą modelu; rozliczenie obejmuje publiczne ceny wywołań narzędzi i 1 kredyt za ukończoną odpowiedź. billing.cost oznacza rezerwację, a billing.refunded obejmuje niewykorzystany budżet. calls podaje opłatę za każde wywołanie. max_credits nigdy nie jest przekraczane. W JSON v1 po wyczerpaniu budżetu odpowiedź zawiera zebrane dowody i wskazuje brakujące dane. Nieznany zakres danych nigdy nie oznacza braku ryzyka. Dane właścicieli i numery KW nie są pobierane ani podawane. Pytania, uwagi źródłowe i rozmowy z modelem nie są zapisywane ani logowane. Cytowania wskazują konkretną odpowiedź źródłową przez wywołanie, wskaźnik JSON i SHA-256 oraz datę i zakres danych. Uwagi źródłowe są niezaufane; tekst modelu nigdy nie jest udostępniany jako dowód faktów. Nagłówek Accept: text/event-stream wybiera zdarzenia SSE według AskEvent. Każde zdarzenie zawiera nazwę i dane JSON. Przed wykonaniem kroku podawane są metoda, ścieżka, parametry, treść żądania i koszt publicznej operacji. Wyniki i kontynuacje są podpisane HMAC-SHA256, związane z kontem i sprawdzane przed naliczeniem opłat. LANDFEED_ASK_HMAC_SECRET wymaga co najmniej 32 bajtów. Zmienione, wygasłe lub należące do innego konta dane są odrzucane. Prześlij ponownie messages, podpisane results z pełną treścią oraz continuation. Do modelu trafia ostatnich 20 wiadomości; wcześniejszy zamiar jest streszczany w bieżącym żądaniu. Wywołania narzędzi aplikacji i oczekiwanie na potwierdzenie nie mają dopłaty za odpowiedź. Tworzenie i usuwanie obserwacji, konfiguracja webhooka, eksport i krok powyżej 3 kredytów wymagają potwierdzenia. Wznowienie wymaga podpisanej kontynuacji oraz pasującego confirmation lub client_results. Zatrzymanie z powodu budżetu zwalnia całą turę z opłat, także wcześniejsze odczyty. Zdarzenia z rozliczeniem kroku są wysyłane po ostatecznym rozliczeniu. W v2 przed rozliczeniem ujawniana jest tylko aktywność kroków; nowe wyniki i fakty są wstrzymywane po zatrzymaniu z powodu budżetu lub błędzie rozliczenia. Rozłączenie nie powoduje automatycznego powtórzenia operacji. Rozmowa nie jest przechowywana. Protokół konsoli opisuje ask/README-v2.md.

## Cost

`x-request-cost`: 1 request per call.

## Inputs

Path/query inputs: none declared.

`q | id | point | lat+lng` at operation level: not specified in the source contract. See declared parameters and request body.

## Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `Accept` | header | false | `{"enum": ["application/json", "text/event-stream"], "type": "string"}` | SSE wybiera ask-v2; JSON zachowuje v1, chyba że podano version=ask-v2 lub pola rozmowy. |

## Request body

```json
{
  "content": {
    "application/json": {
      "example": {
        "context": {
          "parcel_id": "141803_2.0001.12/3"
        },
        "max_credits": 5,
        "question": "Czy działka leży w obszarze zagrożenia powodziowego?"
      },
      "schema": {
        "$ref": "#/components/schemas/AskRequest"
      }
    }
  },
  "required": true
}
```

## Operation credit rule

ask: public tool charges + 1 answer; reserve max_credits, default 5, max 20; disabled/invalid/refusal free

## Authentication

```json
[
  {
    "ApiKey": []
  }
]
```

```json
{
  "ApiKey": {
    "description": "Klucz API konta korzystający z tych samych kredytów co wywołania przez połączenie OAuth.",
    "in": "header",
    "name": "X-Api-Key",
    "type": "apiKey"
  },
  "OAuthConnector": {
    "bearerFormat": "OAuth 2.1 access token",
    "description": "Autoryzacja połączenia OAuth ze wspólnymi kredytami konta. Wykrywanie konfiguracji i zgodę obsługuje usługa uwierzytelniania.",
    "scheme": "bearer",
    "type": "http"
  }
}
```

## Responses

| Status | Description | Billing rule |
| --- | --- | --- |
| 200 | Stan answered, incomplete, budget_exhausted, refused albo stan niedostępności przy wyłączonej lub niedostępnej usłudze; sprawdź status i billing. | See billing.rule and the verbatim Billing policy below (answers, coverage, ambiguity, and batch). |
| 400 | Nieprawidłowe dane wejściowe; bez opłaty. | free_invalid_input |
| 401 | Wymagany ważny klucz konta; bez opłaty. | free_unauthorized |
| 403 | Wymagany klucz przypisany do konta; bez opłaty. | refunded_client_error |
| 413 | Treść przekracza 1 MiB (v2) lub 16 KiB (v1); bez opłaty. | refunded_client_error |
| 429 | Za mało kredytów lub przekroczony limit żądań; bez opłaty. | refunded_client_error |
| 503 | Dostawca lub rozliczenia są niedostępne; sprawdź wywołania i rozliczone kredyty. | refunded_server_error |

### HTTP 200: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    },
    "text/event-stream": {
      "schema": {
        "$ref": "#/components/schemas/AskEvent"
      }
    }
  },
  "description": "Stan answered, incomplete, budget_exhausted, refused albo stan niedostępności przy wyłączonej lub niedostępnej usłudze; sprawdź status i billing."
}
```

### HTTP 400: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    }
  },
  "description": "Nieprawidłowe dane wejściowe; bez opłaty."
}
```

### HTTP 401: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    }
  },
  "description": "Wymagany ważny klucz konta; bez opłaty."
}
```

### HTTP 403: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    }
  },
  "description": "Wymagany klucz przypisany do konta; bez opłaty."
}
```

### HTTP 413: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    }
  },
  "description": "Treść przekracza 1 MiB (v2) lub 16 KiB (v1); bez opłaty."
}
```

### HTTP 429: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    }
  },
  "description": "Za mało kredytów lub przekroczony limit żądań; bez opłaty."
}
```

### HTTP 503: headers and body schemas

```json
{
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AskResponse"
      }
    }
  },
  "description": "Dostawca lub rozliczenia są niedostępne; sprawdź wywołania i rozliczone kredyty."
}
```

## Layer answers and value schemas

When a response includes layer answers, their common frame is:

```json
{
  "additionalProperties": false,
  "allOf": [
    {
      "else": {
        "properties": {
          "value": {
            "type": "object"
          }
        }
      },
      "if": {
        "properties": {
          "coverage": {
            "enum": [
              "not_covered",
              "not_computed"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "value": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "coverage": {
            "enum": [
              "covered",
              "covered_no_data"
            ]
          },
          "layer": {
            "const": "mpzp"
          }
        }
      },
      "then": {
        "properties": {
          "value": {
            "$ref": "#/$defs/MpzpValue"
          }
        }
      }
    }
  ],
  "description": "Wspólny format każdej odpowiedzi tematycznej. Pola właściwe dla danej kategorii znajdują się w value (schematy *Value w openapi.yaml).",
  "properties": {
    "as_of": {
      "format": "date",
      "type": [
        "string",
        "null"
      ]
    },
    "basis": {
      "description": "Przepis lub reguła będąca podstawą wyliczonej odpowiedzi, np. „u.p.z.p. art. 59 ust. 3”.",
      "type": [
        "string",
        "null"
      ]
    },
    "confidence": {
      "maximum": 1,
      "minimum": 0,
      "type": "number"
    },
    "coverage": {
      "$ref": "#/$defs/Coverage"
    },
    "hash": {
      "description": "Skrót sha256 w zapisie szesnastkowym z coverage + ':' + canonical(value) (nagłówek parcel_record.sql). Wartość null dla danych ustalanych podczas żądania.",
      "type": [
        "string",
        "null"
      ]
    },
    "layer": {
      "$ref": "#/$defs/LayerKey"
    },
    "layer_version": {
      "description": "Identyfikator obliczenia dla wcześniej obliczonych danych; live dla danych ustalanych podczas żądania.",
      "type": "string"
    },
    "licence": {
      "$ref": "#/$defs/Licence"
    },
    "method": {
      "$ref": "#/$defs/Method"
    },
    "note": {
      "description": "Jedno zdanie prostym językiem do przytoczenia przez agenta w każdym stanie: co oznacza wartość lub dlaczego jej brakuje. Bez określenia „bezpieczna” i bez „brak planu”, jeżeli nie wskazano rodzaju planu i źródła.",
      "minLength": 1,
      "type": "string"
    },
    "scope": {
      "$ref": "#/$defs/Scope"
    },
    "source": {
      "$ref": "#/$defs/SourceRef"
    },
    "threshold": {
      "maximum": 1,
      "minimum": 0,
      "type": "number"
    },
    "value": {
      "description": "Wartość null wtedy i tylko wtedy, gdy coverage wynosi not_covered lub not_computed.",
      "type": [
        "object",
        "null"
      ]
    }
  },
  "required": [
    "layer",
    "coverage",
    "value",
    "note",
    "source",
    "as_of",
    "layer_version"
  ],
  "type": "object"
}
```

Schema names correspond to the layer keys below. A schema dispatch constraint in `LayerAnswer` is not specified in v1.

| Layer key | LayerAnswer.value schema |
| --- | --- |
| `address` | [AddressValue](../layers/AddressValue.md) |
| `admin` | not specified in the source contract |
| `buildings` | [BuildingsValue](../layers/BuildingsValue.md) |
| `flood` | [FloodValue](../layers/FloodValue.md) |
| `landslide` | [LandslideValue](../layers/LandslideValue.md) |
| `mpzp` | [MpzpValue](../layers/MpzpValue.md) |
| `parcel` | not specified in the source contract |
| `permits` | [PermitsValue](../layers/PermitsValue.md) |
| `planning` | [PlanningValue](../layers/PlanningValue.md) |
| `pog` | [PogValue](../layers/PogValue.md) |
| `prices` | [PricesValue](../layers/PricesValue.md) |
| `protected` | [ProtectedValue](../layers/ProtectedValue.md) |
| `road_access` | [RoadAccessValue](../layers/RoadAccessValue.md) |
| `soil` | [SoilValue](../layers/SoilValue.md) |
| `wz` | [WzValue](../layers/WzValue.md) |

Referenced response definitions from `openapi.yaml`:

See [Schemas](../schemas.md) for all operation-specific definitions.

Envelope references are defined in [Envelope](../envelope.md).

## Coverage states

Cztery stany zakresu danych dla jednej kategorii w danym miejscu. covered: źródło obejmuje to miejsce i zawiera rekord; value zawiera ten rekord. covered_no_data: źródło obejmuje to miejsce, lecz nie zawiera rekordu; value ma określoną dla danej kategorii pustą wartość (false, 0, [], none), nigdy null; jest to potwierdzony brak rekordu, za który naliczana jest opłata. not_covered: źródło nie obejmuje miejsca albo państwo nie publikuje tych danych w postaci do odczytu maszynowego; value=null, agent nie może wyciągać wniosków, a opłata jest zwracana, gdy zapytanie dotyczy tych danych. not_computed: posiadamy źródło, lecz nie obliczyliśmy jeszcze wyniku dla tego miejsca, np. trwa ładowanie zbioru; value=null, opłata jest zwracana. Każdy stan zawiera uwagę napisaną prostym językiem.

States: `covered` | `covered_no_data` | `not_covered` | `not_computed`.

`LayerAnswer` required fields: `layer`, `coverage`, `value`, `note`, `source`, `as_of`, `layer_version`.

## Billing rule

Koszt żądania. charged: liczba naliczonych wywołań po zwrotach. refunded: liczba wywołań naliczonych, a następnie zwróconych. rule: zastosowana reguła. Płatne są odpowiedzi, potwierdzone braki rekordów (covered_no_data) i niejednoznaczne listy kandydatów. Bezpłatne są błędne dane wejściowe (400), brak uwierzytelnienia (401), odpowiedzi bez zmiany danych (304) i operacje bezpłatne. Zwrot obejmuje pozostałe 4xx, każde 5xx, status not_covered oraz odpowiedź dotyczącą jednej kategorii ze stanem not_covered lub not_computed. W operacjach zbiorczych liczby dotyczą poszczególnych elementów.

Rule values: `charged` | `free_operation` | `free_invalid_input` | `free_unauthorized` | `free_not_modified` | `refunded_client_error` | `refunded_server_error` | `refunded_not_covered` | `refunded_not_computed` | `batch_mixed`.

## Recorded example

No valid key supplied. Request UUID and response clock normalized. No database, socket, paid call or live coverage claim.

Recorded HTTP status: 401. The curl example reproduces this admission response with an unset API key. For supported authenticated operations, supply an account key and required permissions; data and billing then depend on that account. The Stripe receiver additionally requires a provider signature.

```sh
curl --silent --show-error --request POST "https://mcp.agentdzialek.pl/v1/ask" \
  -H "X-Api-Key: ${LANDFEED_API_KEY}" \
  -H 'Content-Type: application/json' \
  --data '{"question": "Czy działka leży w obszarze zagrożenia powodziowego?", "context": {"parcel_id": "141803_2.0001.12/3"}, "max_credits": 5}'
```

```json
{
  "answer": "Nie udało się przyjąć pytania. Sprawdź jego treść i podany kontekst.",
  "billing": {
    "charged": 0,
    "cost": 0,
    "credits": 0,
    "refunded": 0,
    "rule": "free_unauthorized"
  },
  "calls": [],
  "citations": [],
  "max_credits": 5,
  "meta": {
    "contract_version": "2.0.0",
    "prompt_version": "ask-v1",
    "request_id": "00000000-0000-4000-8000-000000000000",
    "tools_version": "ask-tools-v1"
  },
  "missing": [],
  "status": "error"
}
```

### Successful contract fixture

Recorded contract output, not a live service observation. Provenance: `landfeed/contracts/fixtures/v2/valid/AskResponse.json`. The parcel and dates belong to this fixture. Dormant operations remain unavailable.

```json
{
  "answer": "Źródło nie wskazuje zagrożenia powodziowego w badanym zakresie. [c1]",
  "billing": {
    "charged": 2,
    "cost": 2,
    "credits": 2,
    "refunded": 0,
    "rule": "charged"
  },
  "calls": [
    {
      "arguments": {
        "id": "141803_2.0001.12/3",
        "layers": [
          "flood"
        ]
      },
      "charged": 1,
      "cost": 1,
      "id": "t1",
      "operation": "parcel",
      "refunded": 0,
      "status": "ok",
      "tool": "parcel"
    }
  ],
  "citations": [
    {
      "answer_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
      "as_of": "2026-10-01",
      "call_id": "t1",
      "coverage": "covered_no_data",
      "id": "c1",
      "layer_key": "flood",
      "method": "official",
      "pointer": "/result/layers/flood",
      "scope": "parcel_polygon",
      "source": {
        "as_of": "2026-10-01",
        "attribution": "Źródło: Państwowe Gospodarstwo Wodne Wody Polskie, mapy zagrożenia powodziowego (ISOK), licencja CC BY 4.0",
        "id": "mzp_flood",
        "title": "mzp_flood"
      }
    }
  ],
  "max_credits": 5,
  "meta": {
    "contract_version": "2.0.0",
    "prompt_version": "ask-v1",
    "request_id": "00000000-0000-4000-8000-000000000001",
    "tools_version": "ask-tools-v1"
  },
  "missing": [],
  "status": "answered"
}
```

## Matching MCP tool

not specified in the source contract
