> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oktavius.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start Guide

> Get up and running with the Oktavius ERP API in minutes

# Quick Start Guide

This guide walks you through the most common integration scenario: pulling extraction results into your ERP or Materialwirtschaft system.

## Prerequisites

* An API token (starting with `okt_live_`)
* Your Auftragsnummer(n) that you want to look up

## Step 1: Verify Your Token

Test that your token works by calling the health endpoint (no auth required) and then a simple list:

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    # Health check (no token needed)
    curl https://api.oktavius.ai/v1/openapi/health

    # List your completed extractions
    curl -X GET "https://api.oktavius.ai/v1/openapi/extractions" \
      -H "Authorization: Bearer okt_live_your_token_here"
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const TOKEN = "okt_live_your_token_here";
    const BASE = "https://api.oktavius.ai/v1/openapi";

    const res = await fetch(`${BASE}/extractions`, {
      headers: { Authorization: `Bearer ${TOKEN}` },
    });

    if (res.status === 401) {
      console.error("Invalid token — check your API key");
    } else {
      const body = await res.json();
      console.log(`${body.pagination.total} extractions available`);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    TOKEN = "okt_live_your_token_here"
    BASE = "https://api.oktavius.ai/v1/openapi"

    res = requests.get(f"{BASE}/extractions",
                       headers={"Authorization": f"Bearer {TOKEN}"})

    if res.status_code == 401:
        print("Invalid token")
    else:
        body = res.json()
        print(f"{body['pagination']['total']} extractions available")
    ```
  </Tab>
</Tabs>

## Step 2: Query by Auftragsnummer

Find all extractions for a specific order. If multiple Regiescheine were uploaded for the same Auftrag, all of them are returned.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET "https://api.oktavius.ai/v1/openapi/extractions?auftragsnummer=22201161" \
      -H "Authorization: Bearer okt_live_your_token_here"
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const res = await fetch(
      `${BASE}/extractions?auftragsnummer=22201161`,
      { headers: { Authorization: `Bearer ${TOKEN}` } }
    );
    const { success, data, pagination } = await res.json();

    for (const extraction of data) {
      console.log(
        `${extraction.auftragsnummer} — ${extraction.positionen_count} positions — ${extraction.status}`
      );
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    res = requests.get(
        f"{BASE}/extractions",
        params={"auftragsnummer": "22201161"},
        headers={"Authorization": f"Bearer {TOKEN}"},
    )
    body = res.json()

    for ext in body["data"]:
        print(f"{ext['auftragsnummer']} — {ext['positionen_count']} positions")
    ```
  </Tab>
</Tabs>

**Example response:**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "completed",
      "auftragsnummer": "22201161",
      "baustelle": "Mokka",
      "completed_at": "2026-03-03T14:00:00Z",
      "created_at": "2026-03-03T13:00:00Z",
      "positionen_count": 4
    }
  ],
  "pagination": { "page": 1, "page_size": 20, "total": 1 }
}
```

## Step 3: Get Full Extraction Detail (Rückimport)

Once you have an extraction ID, fetch the full detail with all line items — this is the data you import back into your system.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET "https://api.oktavius.ai/v1/openapi/extractions/550e8400-e29b-41d4-a716-446655440000" \
      -H "Authorization: Bearer okt_live_your_token_here"
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const extractionId = data[0].id;
    const detailRes = await fetch(`${BASE}/extractions/${extractionId}`, {
      headers: { Authorization: `Bearer ${TOKEN}` },
    });
    const detail = await detailRes.json();

    // Import positions into your system
    for (const pos of detail.data.positionen) {
      console.log(`${pos.menge} ${pos.eh} — ${pos.bezeichnung} (Art. ${pos.artikelnummer})`);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    extraction_id = body["data"][0]["id"]
    detail_res = requests.get(
        f"{BASE}/extractions/{extraction_id}",
        headers={"Authorization": f"Bearer {TOKEN}"},
    )
    detail = detail_res.json()["data"]

    print(f"Auftrag: {detail['auftragsnummer']}")
    print(f"Tätigkeit: {detail['regietaetigkeit']}")

    for pos in detail["positionen"]:
        print(f"  {pos['menge']} {pos['eh']} — {pos['bezeichnung']} (Art. {pos['artikelnummer']})")

    if detail["gesamtuebersicht"]:
        print(f"Gesamt: {detail['gesamtuebersicht']['summe_gesamt']}")
    ```
  </Tab>
</Tabs>

**Example detail response:**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "completed_at": "2026-03-03T14:00:00Z",
    "created_at": "2026-03-03T13:00:00Z",
    "auftragsnummer": "22201161",
    "baustelle": "Mokka",
    "regietaetigkeit": "Aufgrund von mehrmaligen Planänderungen. Kabel eingezogen.",
    "positionen": [
      {
        "kategorie": "ARBEIT",
        "artikelnummer": "",
        "bezeichnung": "Obermonteurstunden",
        "menge": 55,
        "eh": "Std",
        "preis_einheit": 80.5,
        "positionspreis": 4427.5
      },
      {
        "kategorie": "MATERIAL",
        "artikelnummer": "12011900716",
        "bezeichnung": "NYM-J 5x2,5mm²",
        "menge": 500,
        "eh": "m",
        "preis_einheit": 1.15,
        "positionspreis": 575
      }
    ],
    "gesamtuebersicht": {
      "summe_arbeit": 4427.5,
      "summe_material": 836.75,
      "summe_gesamt": 5264.24
    }
  }
}
```

### Field Mapping for Your System

Based on the "Rückimport" requirements:

| Your Field                             | API Field                    | Notes                            |
| -------------------------------------- | ---------------------------- | -------------------------------- |
| **Auftragsnummer** (required)          | `data.auftragsnummer`        | Project/order number             |
| **Text der durchgeführten Arbeiten**   | `data.regietaetigkeit`       | May be null if not entered       |
| **Position: Menge** (required)         | `positionen[].menge`         | Quantity                         |
| **Position: Artikelnummer** (required) | `positionen[].artikelnummer` | May be empty if no article match |

<Warning>
  When `artikelnummer` is an empty string, the technician entered free text that could not be
  matched to an article in the catalog. Your system should handle this case (e.g. flag for manual
  resolution).
</Warning>

## Step 4: Poll for New Results

To continuously sync new extractions, use the `since` parameter with the timestamp of your last successful sync:

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET "https://api.oktavius.ai/v1/openapi/extractions?since=2026-03-03T00:00:00Z" \
      -H "Authorization: Bearer okt_live_your_token_here"
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    let lastSync = new Date("2026-03-03T00:00:00Z");

    async function syncNewExtractions() {
      const res = await fetch(
        `${BASE}/extractions?since=${lastSync.toISOString()}`,
        { headers: { Authorization: `Bearer ${TOKEN}` } }
      );
      const { data } = await res.json();

      for (const extraction of data) {
        const detail = await fetchDetail(extraction.id);
        importToYourSystem(detail);
      }

      if (data.length > 0) {
        lastSync = new Date();
      }
    }

    // Run every 5 minutes
    setInterval(syncNewExtractions, 5 * 60 * 1000);
    ```
  </Tab>
</Tabs>

## Error Handling

All error responses follow the same format:

```json theme={null}
{
  "success": false,
  "error": "Unauthorized",
  "message": "Invalid API token."
}
```

| Status | Meaning                                                       |
| ------ | ------------------------------------------------------------- |
| `400`  | Invalid request parameters (e.g. unknown status value)        |
| `401`  | Invalid, expired, or missing API token                        |
| `403`  | Token not associated with an organization                     |
| `404`  | Extraction not found (or belongs to a different organization) |
| `429`  | Rate limit exceeded (1,000 requests/hour)                     |
| `500`  | Internal server error                                         |
