Sări la conținutul principal

Fișier de Context LLM

O referință de integrare pe o singură pagină concepută pentru a fi lipită într-un asistent IA (Claude, GPT-4, Gemini, etc.) ca context înainte de a-i cere să implementeze o integrare cu API Qarta.

Conține tot ce are nevoie un LLM pentru a scrie cod corect prima dată: URL de bază, formatul exact al header-ului de autentificare, ambele formate de formă, diferențele de filtre V1/V2, toate cele 36 de endpoint-uri, formate de ieșire, formate de eroare, reguli de paginare și un tabel cu greșeli comune.

Cum să o folosești

  1. Descarcă sau copiază conținutul fișierului mai jos
  2. Lipește-l la începutul promptului tău, înainte de cererea de implementare
  3. LLM-ul o va folosi ca referință autoritară în loc să ghicească

Exemplu de prompt:

<context>
[lipește conținutul CLAUDE.md aici]
</context>

Folosind API-ul documentat mai sus, scrie o funcție Python care primește
un poligon GeoJSON și returnează toate punctele de portofoliu din el ca DataFrame.

Descarcă

Descarcă CLAUDE.md

Conținutul fișierului

Copiază markdown-ul brut de mai jos folosind butonul din dreapta sus al blocului de cod.

# Qarta API — Integration Reference

You are implementing an integration against the **Qarta API** (also called the Layers API), a geospatial intelligence platform by Quarticle. This file contains everything you need to implement correctly. Do not guess — use the facts below.

---

## Base URL and Authentication

```
Base URL: https://graph.quarticle.ro
Auth: Authorization: <api_key>
```

**Do NOT add a `Bearer` prefix.** The raw API key goes directly in the `Authorization` header.

```bash
curl https://graph.quarticle.ro/graph/api/v1/places/geocode?q=London \
-H "Authorization: YOUR_API_KEY"
```

---

## The `source` Parameter

All portfolio, entries, points, accumulation, and risk endpoints require a `source` parameter. It identifies which data layer/portfolio to query — a string identifier provisioned to the account (e.g., `"portfolio_01"`).

Discover available sources by calling the provision endpoint first:

```bash
GET /graph/api/v1/reports/portfolio/provision
```

Response structure:
```json
{
"provisioning": {
"portfolio_01": {
"columns": [
{ "field": "insured_value", "type": "number" },
{ "field": "property_type", "type": "string" }
],
"filters": [
{ "field": "oe_id", "label": "Organization", "required": true, "values": ["org_1"] }
],
"reports": { "accumulation": { "enabled": true } }
}
}
}
```

Use the key (`"portfolio_01"`) as the `source` value in all subsequent calls.

---

## Shape Formats

There are **two distinct shape formats** — do not mix them up.

### Polygon shapes (for `/selection` endpoints)

Standard GeoJSON Feature. Coordinates are `[longitude, latitude]` arrays (longitude first).

```json
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[-74.0200, 40.7000],
[-74.0000, 40.7000],
[-74.0000, 40.7300],
[-74.0200, 40.7300],
[-74.0200, 40.7000]
]]
},
"properties": {}
}
```

Rules:
- First and last coordinate must be identical (closes the polygon)
- Minimum 4 coordinate pairs (3 unique + 1 closing)
- `MultiPolygon` is also supported
- Coordinate system: WGS84 / EPSG:4326 only

### Buffer shapes (for `/buffer-selection` and `/buffer` endpoints)

**Not GeoJSON.** Uses a different format with named `{lat, long}` objects. Coordinates are **latitude first** in named fields (opposite order from GeoJSON).

```json
{
"shape": {
"type": "circle",
"coordinates": [{ "lat": 40.7128, "long": -74.0060 }]
},
"radiuses": [1000, 5000, 10000]
}
```

Rules:
- `radiuses` is at the **request body level**, not inside the shape object
- Values are in **meters**
- Multiple radii return one result set per radius
- `coordinates` uses `{ "lat": ..., "long": ... }` — not arrays, not GeoJSON order

---

## Filters: V1 vs V2

### V1 filters — simple flat array

```json
{
"source": "portfolio_01",
"filters": [
{ "field": "oe_id", "value": "my_org" },
{ "field": "property_type", "value": "residential" }
]
}
```

All filters are AND-combined. No nesting.

### V2 filters — recursive nested tree

```json
{
"source": "portfolio_01",
"filters_v2": [
{
"field": "property_type",
"value": "residential",
"filters": [
{ "field": "occupancy", "value": "occupied" }
]
}
]
}
```

### ⚠️ V2 search endpoint exception

The V2 search endpoints (`/graph/api/v2/reports/portfolio/entries/selection/search` and the points equivalent) use `"filters"` as the body key — **not** `"filters_v2"`. All other V2 endpoints use `"filters_v2"`.

---

## All Endpoints

### Location Services (no `source` required)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/graph/api/v1/places/autocomplete?q=` | Address suggestions |
| GET | `/graph/api/v1/places/geocode?q=` | Address → coordinates |
| GET | `/graph/api/v1/places/geocode/reverse?lat=&lon=` | Coordinates → address |

Geocode response:
```json
{ "lat": 51.5034, "lon": -0.1276, "label": "10 Downing St...", "geocodingQuality": "rooftop" }
```

### Portfolio — Provision

| Method | Path | Description |
|--------|------|-------------|
| GET | `/graph/api/v1/reports/portfolio/provision?source=` | Source config, columns, filters |
| GET | `/graph/api/v1/reports/portfolio/provision/filters?source=` | Filter hierarchy |

### Portfolio — V1 Selection (POST, body uses `filters`)

| Method | Path | Description |
|--------|------|-------------|
| POST | `/graph/api/v1/reports/portfolio/points/selection` | GeoJSON points in polygon |
| POST | `/graph/api/v1/reports/portfolio/points/buffer-selection` | GeoJSON points in buffer |
| POST | `/graph/api/v1/reports/portfolio/entries/selection` | Detailed records in polygon |
| POST | `/graph/api/v1/reports/portfolio/entries/buffer-selection` | Detailed records in buffer |
| GET | `/graph/api/v1/reports/portfolio/entries/selection/search` | Search entries (query params) |
| GET | `/graph/api/v1/reports/portfolio/points/attributes?source=&value=` | Attributes for a point |

### Portfolio — V1 Accumulation (POST, body uses `filters`)

| Method | Path | Description |
|--------|------|-------------|
| POST | `/graph/api/v1/reports/portfolio/accumulation/selection` | Aggregate metrics in polygon |
| POST | `/graph/api/v1/reports/portfolio/accumulation/buffer` | Aggregate metrics in buffer |
| POST | `/graph/api/v1/reports/portfolio/accumulation/layer` | Layer-level data retrieval |

### Portfolio — V2 (POST, body uses `filters_v2` except search)

Replace `/v1/` with `/v2/` for all selection and accumulation endpoints above. Search endpoints become POST:

| Method | Path |
|--------|------|
| POST | `/graph/api/v2/reports/portfolio/entries/selection/search` |
| POST | `/graph/api/v2/reports/portfolio/points/selection/search` |

### Risk Reporting

| Method | Path | Description |
|--------|------|-------------|
| POST | `/graph/api/v1/reports/risk/point` | Risk report for a point (PDF/HTML) |
| POST | `/graph/api/v1/reports/portfolio/risk/point-buffer` | Risk report with buffer accumulation |

### Point Enrichment

| Method | Path | Description |
|--------|------|-------------|
| GET | `/graph/featureinfo/risk-lookup/api/v1/bfi/single?lat=&lng=&layers=&operation=` | Single point |
| POST | `/graph/featureinfo/enrichment/api/v1/bfi/batch` | Batch enrichment |

### Web Services (WMS / WFS)

| Method | Path | Key params |
|--------|------|------------|
| GET | `/graph/layers/wms?SERVICE=WMS&REQUEST=GetCapabilities` | Capabilities XML |
| GET | `/graph/layers/wms?SERVICE=WMS&REQUEST=GetMap&...` | Map tile (PNG) |
| GET | `/graph/layers/wms?SERVICE=WMS&REQUEST=GetFeatureInfo&...` | Feature info at pixel |
| GET | `/graph/layers/wms?SERVICE=WMS&REQUEST=GetLegendGraphic&LAYER=` | Legend image |
| GET | `/graph/layers/wms?SERVICE=WMS&REQUEST=DescribeLayer&LAYERS=` | Layer description |
| GET | `/graph/layers/wfs?SERVICE=WFS&REQUEST=GetFeature&TYPENAMES=` | Feature data |

WMS map tiles use **Web Mercator (EPSG:3857)** for the `SRS`/`CRS` and `BBOX` parameters. WFS feature queries use EPSG:4326.

### Live Events

| Method | Path |
|--------|------|
| POST | `/graph/api/v1/live-events/footprints/:footprint/enrich/entries` |

---

## Output Formats

Control the response format with the `Accept` header:

| Accept header | Format | Supported by |
|---------------|--------|--------------|
| `application/json` | JSON (default) | All endpoints |
| `text/csv` | CSV | entries and points endpoints |
| `application/pdf` | PDF | risk report endpoints |
| `text/html` | HTML | risk report endpoints |
| `image/png` | PNG image | WMS GetMap, GetLegendGraphic |
| `application/xml` | XML | WMS/WFS GetCapabilities |

---

## Pagination

Used on entries endpoints with `columns`, `limit`, and `page`:

```json
{
"source": "portfolio_01",
"shape": { ... },
"columns": ["id", "name", "value"],
"limit": 100,
"page": 0
}
```

- `page` is **0-indexed**
- Increment `page` until the response returns fewer items than `limit`

---

## Error Response Formats

Two formats exist — which one you receive depends on the endpoint:

```json
{ "message": "Error description" }
```
```json
{ "errorMessage": { "message": "Error description" } }
```

Handle both. HTTP status codes:

| Code | Meaning |
|------|---------|
| 400 | Invalid parameters or malformed body |
| 401 | Missing or invalid API key |
| 403 | Invalid shape geometry or insufficient permissions |
| 500 | Server error — retry with exponential backoff |

A 403 on a spatial endpoint usually means the GeoJSON shape is invalid (wrong coordinate order, unclosed polygon, wrong CRS).

---

## Python SDK

A fully-typed SDK is available at `sdks/layers/` in this repository (also downloadable as `layers-sdk-python.zip`).

**Requirements:** Python ≥ 3.10, `httpx ≥ 0.27`, `pydantic ≥ 2.0`

**Installation:**
```bash
pip install . # from the unzipped SDK directory
# or
pip install httpx pydantic && # place layers_sdk/ in your project
```

**Client setup:**
```python
from layers_sdk import LayersClient
client = LayersClient(base_url="https://graph.quarticle.ro", api_key="YOUR_API_KEY")
```

**Service map:**

| `client.*` | Covers |
|------------|--------|
| `location` | `autocomplete()`, `geocode()`, `reverse_geocode()` |
| `reports_v1` | V1 portfolio — uses `PortfolioFilter` |
| `reports_v2` | V2 portfolio — uses `RequestFilter` |
| `risk` | `point_risk()`, `point_buffer_risk()`, V2 variants |
| `enrichment` | `enrich_single()`, `enrich_batch()` |
| `wms` | `get_map()`, `get_capabilities()`, `get_feature_info()`, `get_legend_graphic()` |
| `wfs` | `get_feature()` |

**Key model imports:**
```python
from layers_sdk import (
LayersClient,
Feature, Geometry, GeometryType, # polygon shapes
Buffer, Coordinates, # buffer shapes
PortfolioFilter, # V1 filters
RequestFilter, # V2 filters
PointRiskRequestPayload, PointRiskLayers, # risk reports
PointRequest, # batch enrichment
)
from layers_sdk.exceptions import BadRequestError, ForbiddenError, ServerError
```

**Prefer the SDK over raw HTTP** for any Python integration — it handles model validation, serialisation, and raises typed exceptions.

---

## Common Mistakes to Avoid

| Mistake | Correct behaviour |
|---------|-------------------|
| Adding `Bearer ` prefix to the API key | Send the raw key value directly |
| Using `[lat, lon]` coordinate order in GeoJSON | GeoJSON is always `[lon, lat]` |
| Using GeoJSON format for buffer shapes | Buffer shapes use `{type: "circle", coordinates: [{lat, long}]}` |
| Putting `radiuses` inside the shape object | `radiuses` is a top-level request body field |
| Using `filters_v2` in V2 search endpoint body | V2 search uses `filters`, not `filters_v2` |
| Using `GET /v1/status` to test connectivity | That endpoint does not exist — use geocode instead |
| Forgetting to close polygon rings | First and last coordinate must be identical |
| Using UTM or Web Mercator coordinates in shapes | Shapes always use WGS84 / EPSG:4326 |
| Expecting a single error response format | Handle both `{message}` and `{errorMessage: {message}}` |