{
 "openapi": "3.1.0",
 "info": {
  "title": "IvaBot API",
  "version": "1.0.0",
  "summary": "Pristup Ivi (AI asistent podrške) za integracije partnera — server-server.",
  "description": "**Okruženja.** `test` = sandbox: lažni FAQ i lažni chat, simuliran trošak, bez modela, bez pravog znanja i bez ličnih podataka — za razvoj integracije. `live` = pravi objavljeni javni FAQ (`faq:read`). Live chat (`chat:write` sa `live` ključem) dolazi u kasnijoj fazi; do tada `live` ključ ne može dobiti `chat:write`. Dok live nije uključen za projekat, live zahtjevi vraćaju `503 policy_unavailable`.\n\n**Autentikacija:** `Authorization: Bearer <API ključ>`. Ključ je samo za server — nikad u browser, mobilnu aplikaciju, URL ili log. CORS je zatvoren. Format ključa: `iva_<test|live>_<projekat>_<kid>_<tajna>`; prefiks služi i za automatsko prepoznavanje procurjelih ključeva.\n\n**Dozvole (scope):** `faq:read` (objavljeni javni FAQ), `chat:write` (razgovor sa Ivom bez profila). Scope-ovi su nezavisni.\n\n**Bez identiteta kupca:** v1 ne prima korisnika, mail, telefon ni token prijave — takva polja vraćaju `400 unknown_field`. Iva u v1 odgovara samo iz javnog znanja, bez pristupa profilu, bez predaje kolegi i bez radnji.\n\n**Greške:** `application/problem+json` (RFC 9457) sa poljima `type`, `title`, `status`, `code`, `request_id`.\n\n**Limiti:** zaglavlja `X-Iva-RateLimit-Limit`, `X-Iva-RateLimit-Remaining`, `X-Iva-RateLimit-Reset` (UTC epoch sekunde); kod 429/503 `Retry-After` u sekundama. Trošak AI-ja je ograničen po ključu (po zahtjevu, dnevno, mjesečno; dnevni/mjesečni prozor po Europe/Sarajevo). Budžet se rezerviše prije obrade.\n\n**Ponavljanje:** svaki `POST` traži `Idempotency-Key`. Kod mrežne greške prvo provjerite status poruke (`GET .../messages/{message_id}`), tek onda šaljite novi zahtjev.\n\n**Verzije:** `/v1` garantuje šemu i značenje polja, ne isti AI tekst. Promjena koja lomi integraciju ide u `/v2` uz najmanje 90 dana prelaza.\n\n**Čuvanje:** razgovori 7 dana (24 h neaktivnosti zatvara razgovor), idempotentni odgovori 24 h, audit 90 dana (bez sadržaja poruka).",
  "contact": {
   "name": "IvaBot",
   "url": "https://ivabot.ai"
  }
 },
 "servers": [
  {
   "url": "https://api.ivabot.ai",
   "description": "Produkcija"
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "tags": [
  {
   "name": "FAQ"
  },
  {
   "name": "Razgovor"
  },
  {
   "name": "Sistem"
  }
 ],
 "paths": {
  "/healthz": {
   "get": {
    "tags": [
     "Sistem"
    ],
    "summary": "Da li servis radi",
    "security": [],
    "operationId": "health",
    "responses": {
     "200": {
      "description": "Radi",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "status": {
           "const": "ok"
          }
         }
        },
        "example": {
         "status": "ok"
        }
       }
      }
     }
    }
   }
  },
  "/v1/faq": {
   "get": {
    "tags": [
     "FAQ"
    ],
    "summary": "Lista / pretraga objavljenog FAQ-a",
    "operationId": "listFaq",
    "x-scope": "faq:read",
    "parameters": [
     {
      "name": "query",
      "in": "query",
      "schema": {
       "type": "string",
       "maxLength": 200
      },
      "description": "Sve riječi (≥3 slova) moraju postojati; bez obzira na dijakritike."
     },
     {
      "name": "cursor",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "`next_cursor` iz prethodnog odgovora."
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 50,
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Stranica rezultata",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/FaqList"
        },
        "example": {
         "data": [
          {
           "id": "test-sifra",
           "question": "Kako promijeniti šifru? (TEST)",
           "answer_text": "Ovo je testni odgovor sandboxa.",
           "url": "https://api.ivabot.ai/docs#sandbox"
          }
         ],
         "next_cursor": null,
         "knowledge_version": "test-sandbox-1",
         "environment": "test",
         "request_id": "req_0f3c…"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     },
     "503": {
      "$ref": "#/components/responses/Unavailable"
     }
    }
   }
  },
  "/v1/faq/{id}": {
   "get": {
    "tags": [
     "FAQ"
    ],
    "summary": "Jedno FAQ pitanje",
    "operationId": "getFaq",
    "x-scope": "faq:read",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[A-Za-z0-9_-]{1,80}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Pitanje",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/FaqItemResponse"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     },
     "503": {
      "$ref": "#/components/responses/Unavailable"
     }
    }
   }
  },
  "/v1/conversations": {
   "post": {
    "tags": [
     "Razgovor"
    ],
    "summary": "Novi razgovor sa Ivom",
    "operationId": "createConversation",
    "x-scope": "chat:write",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "description": "Jedinstven po radnji (8–128 znakova `A-Za-z0-9_.:-`). Isti ključ + isto tijelo = isti odgovor 24 h; drugo tijelo = 409.",
      "schema": {
       "type": "string",
       "minLength": 8,
       "maxLength": 128,
       "pattern": "^[A-Za-z0-9_.:-]+$"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConversationCreate"
       },
       "example": {}
      }
     }
    },
    "responses": {
     "201": {
      "description": "Razgovor otvoren",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Conversation"
        },
        "example": {
         "id": "conv_Xy7…",
         "environment": "test",
         "expires_at": 1793000000,
         "max_messages": 30,
         "request_id": "req_…"
        }
       }
      }
     },
     "409": {
      "$ref": "#/components/responses/Conflict"
     },
     "413": {
      "$ref": "#/components/responses/TooLarge"
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMedia"
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     },
     "503": {
      "$ref": "#/components/responses/Unavailable"
     }
    }
   }
  },
  "/v1/conversations/{id}/messages": {
   "post": {
    "tags": [
     "Razgovor"
    ],
    "summary": "Pošalji poruku i dobij Ivin odgovor",
    "operationId": "sendMessage",
    "x-scope": "chat:write",
    "description": "Jedna poruka istovremeno po razgovoru (`409 conversation_busy`). Najviše 30 poruka po razgovoru. Nema streaminga u v1. Trošak se rezerviše prije obrade (`429 budget_exhausted` ako nema budžeta).",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[A-Za-z0-9_-]{1,80}$"
      }
     },
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "description": "Jedinstven po radnji (8–128 znakova `A-Za-z0-9_.:-`). Isti ključ + isto tijelo = isti odgovor 24 h; drugo tijelo = 409.",
      "schema": {
       "type": "string",
       "minLength": 8,
       "maxLength": 128,
       "pattern": "^[A-Za-z0-9_.:-]+$"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MessageCreate"
       },
       "example": {
        "text": "Kako da promijenim šifru?"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Ivin odgovor",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Message"
        },
        "example": {
         "id": "msg_Ab3…",
         "conversation_id": "conv_Xy7…",
         "status": "completed",
         "answer": {
          "text": "[TEST] Ovo je odgovor sandboxa …",
          "sources": [
           {
            "faq_id": "test-sifra",
            "url": "https://api.ivabot.ai/docs#sandbox",
            "title": "Kako promijeniti šifru? (TEST)"
           }
          ]
         },
         "knowledge_version": "test-sandbox-1",
         "environment": "test",
         "request_id": "req_…"
        }
       }
      }
     },
     "409": {
      "$ref": "#/components/responses/Conflict"
     },
     "413": {
      "$ref": "#/components/responses/TooLarge"
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMedia"
     },
     "502": {
      "$ref": "#/components/responses/Upstream"
     },
     "504": {
      "$ref": "#/components/responses/Upstream"
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     },
     "503": {
      "$ref": "#/components/responses/Unavailable"
     }
    }
   }
  },
  "/v1/conversations/{id}/messages/{message_id}": {
   "get": {
    "tags": [
     "Razgovor"
    ],
    "summary": "Status / ponovno čitanje vlastitog odgovora",
    "operationId": "getMessage",
    "x-scope": "chat:write",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[A-Za-z0-9_-]{1,80}$"
      }
     },
     {
      "name": "message_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[A-Za-z0-9_-]{1,80}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Odgovor (`completed`) ili `indeterminate` (ishod nepoznat — ne šaljite automatski ponovo)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Message"
        }
       }
      }
     },
     "202": {
      "description": "Još u obradi — pokušajte nakon `Retry-After`",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/MessageStatus"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     },
     "503": {
      "$ref": "#/components/responses/Unavailable"
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "API ključ iz IvaBot admina. Primjer oblika (nije pravi): `iva_test_pik_0123456789abcdef0123456789abcdef_PRIMJER-nije-pravi-kljuc-xxxxxxxxxxxxxxxxx`"
   }
  },
  "schemas": {
   "Problem": {
    "type": "object",
    "required": [
     "type",
     "title",
     "status",
     "code",
     "request_id"
    ],
    "properties": {
     "type": {
      "type": "string",
      "format": "uri"
     },
     "title": {
      "type": "string"
     },
     "status": {
      "type": "integer"
     },
     "request_id": {
      "type": "string"
     },
     "code": {
      "type": "string",
      "enum": [
       "invalid_request",
       "unknown_field",
       "invalid_api_key",
       "forbidden",
       "insufficient_scope",
       "not_found",
       "method_not_allowed",
       "idempotency_conflict",
       "conversation_busy",
       "conversation_limit",
       "payload_too_large",
       "unsupported_media_type",
       "rate_limited",
       "budget_exhausted",
       "upstream_failure",
       "unavailable",
       "policy_unavailable",
       "budget_state_unknown",
       "upstream_timeout",
       "internal_error"
      ]
     }
    }
   },
   "FaqItem": {
    "type": "object",
    "required": [
     "id",
     "question",
     "answer_text",
     "url"
    ],
    "properties": {
     "id": {
      "type": "string"
     },
     "question": {
      "type": "string"
     },
     "answer_text": {
      "type": "string",
      "description": "Mini-markup: prazna linija = pasus, `- ` lista, `**x**` bold. Prikazati kao tekst, ne kao HTML."
     },
     "url": {
      "type": "string",
      "format": "uri"
     }
    }
   },
   "FaqItemResponse": {
    "allOf": [
     {
      "$ref": "#/components/schemas/FaqItem"
     },
     {
      "type": "object",
      "properties": {
       "knowledge_version": {
        "type": "string"
       },
       "environment": {
        "enum": [
         "test",
         "live"
        ]
       },
       "request_id": {
        "type": "string"
       }
      }
     }
    ]
   },
   "FaqList": {
    "type": "object",
    "required": [
     "data",
     "next_cursor",
     "knowledge_version",
     "environment",
     "request_id"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/FaqItem"
      }
     },
     "next_cursor": {
      "type": [
       "string",
       "null"
      ]
     },
     "knowledge_version": {
      "type": "string"
     },
     "environment": {
      "enum": [
       "test",
       "live"
      ]
     },
     "request_id": {
      "type": "string"
     }
    }
   },
   "ConversationCreate": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
     "locale": {
      "const": "bs-BA"
     }
    }
   },
   "Conversation": {
    "type": "object",
    "required": [
     "id",
     "environment",
     "expires_at",
     "request_id"
    ],
    "properties": {
     "id": {
      "type": "string"
     },
     "environment": {
      "enum": [
       "test",
       "live"
      ]
     },
     "expires_at": {
      "type": "integer",
      "description": "UTC epoch sekunde"
     },
     "max_messages": {
      "type": "integer"
     },
     "request_id": {
      "type": "string"
     }
    }
   },
   "MessageCreate": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "text"
    ],
    "properties": {
     "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 4000
     }
    }
   },
   "Source": {
    "type": "object",
    "required": [
     "faq_id",
     "url",
     "title"
    ],
    "properties": {
     "faq_id": {
      "type": "string"
     },
     "url": {
      "type": "string",
      "format": "uri"
     },
     "title": {
      "type": "string"
     }
    }
   },
   "Message": {
    "type": "object",
    "required": [
     "id",
     "conversation_id",
     "status",
     "request_id"
    ],
    "properties": {
     "id": {
      "type": "string"
     },
     "conversation_id": {
      "type": "string"
     },
     "status": {
      "enum": [
       "completed",
       "indeterminate"
      ]
     },
     "answer": {
      "type": "object",
      "properties": {
       "text": {
        "type": "string",
        "description": "Čist tekst; nikad ga ne izvršavati kao HTML."
       },
       "sources": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/Source"
        }
       }
      }
     },
     "knowledge_version": {
      "type": "string"
     },
     "environment": {
      "enum": [
       "test",
       "live"
      ]
     },
     "request_id": {
      "type": "string"
     }
    }
   },
   "MessageStatus": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "conversation_id": {
      "type": "string"
     },
     "status": {
      "const": "processing"
     },
     "request_id": {
      "type": "string"
     }
    }
   }
  },
  "responses": {
   "BadRequest": {
    "description": "Neispravan zahtjev ili nepoznato polje",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "Unauthorized": {
    "description": "Nedostaje/neispravan ključ (zaglavlje WWW-Authenticate)",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "Forbidden": {
    "description": "Nedostaje scope ili adresa nije dozvoljena",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "NotFound": {
    "description": "Ne postoji (isto za tuđe, isteklo ili pogrešno okruženje)",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "Conflict": {
    "description": "Idempotency konflikt, razgovor zauzet ili pun",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "TooLarge": {
    "description": "Tijelo veće od 16 KiB",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "UnsupportedMedia": {
    "description": "Content-Type nije application/json",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "TooManyRequests": {
    "description": "Limit zahtjeva ili budžeta (Retry-After)",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "Upstream": {
    "description": "Greška/timeout AI servisa (ne znači da trošak nije nastao)",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   },
   "Unavailable": {
    "description": "Privremeno nedostupno ili okruženje nije uključeno (Retry-After)",
    "content": {
     "application/problem+json": {
      "schema": {
       "$ref": "#/components/schemas/Problem"
      }
     }
    }
   }
  }
 }
}