{
  "openapi": "3.1.0",
  "info": {
    "title": "CrioX PBX API",
    "version": "1.1.0",
    "description": "Istoricul apelurilor centralei tale, din aplicația ta. Cheia se face în CrioX, la Integrări → API, și se trimite în antetul Authorization. Documentația completă: https://docs.criox.ro",
    "contact": {
      "name": "CrioX",
      "email": "suport@criox.ro",
      "url": "https://docs.criox.ro"
    }
  },
  "servers": [
    {
      "url": "https://api.criox.ro/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "cont",
      "description": "Contul și cheia cu care întrebi."
    },
    {
      "name": "apeluri",
      "description": "Istoricul apelurilor: cele primite și cele date, cu cineva din afară."
    }
  ],
  "paths": {
    "/account": {
      "get": {
        "operationId": "getAccount",
        "tags": [
          "cont"
        ],
        "summary": "Cine sunt",
        "description": "Contul și cheia cu care ai întrebat. Nu cere nimic de la centrală, deci spune pe loc dacă cheia merge.",
        "responses": {
          "200": {
            "description": "Contul.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/calls": {
      "get": {
        "operationId": "listCalls",
        "tags": [
          "apeluri"
        ],
        "summary": "Lista apelurilor",
        "description": "Apelurile, cele mai noi întâi, pe pagini. Fără `from`, ultimele 30 de zile. Apelurile interne (între două interioare) nu apar.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "De la acest moment (inclusiv). ISO 8601 sau secunde Unix.",
            "schema": {
              "type": "string",
              "examples": [
                "2026-10-01T00:00:00+03:00",
                "1759266000"
              ]
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Până la acest moment. ISO 8601 sau secunde Unix.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Doar primite sau doar date.",
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound"
              ]
            }
          },
          {
            "name": "result",
            "in": "query",
            "description": "Doar apelurile cu acest rezultat. `missed` = primite la care nu a vorbit nimeni, oricare ar fi cauza.",
            "schema": {
              "type": "string",
              "enum": [
                "answered",
                "missed",
                "busy",
                "no_answer",
                "cancelled",
                "failed"
              ]
            }
          },
          {
            "name": "number",
            "in": "query",
            "description": "Numărul celui din afară, sau o parte din el (cifre; `+`, spațiile și punctele se ignoră).",
            "schema": {
              "type": "string",
              "examples": [
                "0743755549",
                "+40743755549"
              ]
            }
          },
          {
            "name": "line",
            "in": "query",
            "description": "Numărul tău pe care a intrat sau de pe care a ieșit apelul (cifre).",
            "schema": {
              "type": "string",
              "examples": [
                "0215708509"
              ]
            }
          },
          {
            "name": "agent",
            "in": "query",
            "description": "Interiorul colegului care a vorbit (a răspuns sau a format).",
            "schema": {
              "type": "string",
              "examples": [
                "101"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Câte apeluri pe pagină.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` din pagina anterioară. Se trimite neschimbat.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "O pagină de apeluri.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/calls/{id}": {
      "get": {
        "operationId": "getCall",
        "tags": [
          "apeluri"
        ],
        "summary": "Un apel",
        "description": "Un apel după id, cât timp îl ține retenția contului. Același obiect ca în listă și ca în evenimentele de webhook (acolo id-ul apelului e `call_id`).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id-ul apelului (UUID).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Apelul.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "cxk_…",
        "description": "Cheia din Integrări → API, în antetul `Authorization: Bearer cxk_…`."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Un parametru e scris greșit. `message` spune care.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Cheia lipsește, nu există sau a fost revocată.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Contul e suspendat sau reziliat.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Nu există.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Prea multe cereri pe minut. `Retry-After` spune câte secunde să aștepți.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "Centrala nu răspunde acum. Reîncearcă peste câteva secunde.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Codul, stabil, de comparat în program.",
            "examples": [
              "invalid_parameter"
            ]
          },
          "message": {
            "type": "string",
            "description": "Explicația, pentru cine citește jurnalul.",
            "examples": [
              "„direction” poate fi doar: inbound, outbound."
            ]
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "account_id",
          "account_name",
          "key_name",
          "key_prefix",
          "scopes",
          "api_version"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "Id-ul contului tău în CrioX. Același ca `account_id` din evenimente."
          },
          "account_name": {
            "type": "string",
            "description": "Numele contului."
          },
          "key_name": {
            "type": "string",
            "description": "Numele pe care l-ai dat cheii. Poate fi gol."
          },
          "key_prefix": {
            "type": "string",
            "description": "Primele 12 caractere ale cheii, cum apar în listă.",
            "examples": [
              "cxk_7Qp2mX9a"
            ]
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "calls:read"
              ]
            },
            "description": "Ce are voie cheia."
          },
          "api_version": {
            "type": "integer",
            "const": 1
          }
        }
      },
      "CallList": {
        "type": "object",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Call"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Mai sunt apeluri după această pagină. Pagina poate avea mai puține rânduri decât `limit` și totuși `has_more: true`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Se trimite ca `cursor` pentru pagina următoare. `null` când nu mai e nimic."
          }
        }
      },
      "Call": {
        "type": "object",
        "description": "Un apel cu cineva din afară. Aceleași chei și aceleași valori ca în evenimentele de webhook; acolo `id` se numește `call_id`.",
        "required": [
          "id",
          "direction",
          "started_at",
          "ended_at",
          "contact_number",
          "contact_number_national",
          "contact_name",
          "line_number",
          "line_number_national",
          "agent_extension",
          "agent_name",
          "queue_number",
          "queue_name",
          "result",
          "missed_reason",
          "after_hours",
          "wait_seconds",
          "talk_seconds",
          "duration_seconds",
          "recorded",
          "crm_provider",
          "crm_contact_id",
          "crm_contact_url",
          "destination_type",
          "destination_number",
          "destination_name",
          "path"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Id-ul apelului. În evenimentele de webhook e `call_id`."
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ],
            "description": "`inbound` = primit, `outbound` = dat."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "description": "Începutul apelului, UTC."
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "description": "Sfârșitul apelului, UTC."
          },
          "contact_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numărul celui din afară, internațional (`+40…`). `null` când e ascuns."
          },
          "contact_number_national": {
            "type": [
              "string",
              "null"
            ],
            "description": "Același număr, național (`07…`)."
          },
          "contact_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numele lui, din CRM sau din agenda CrioX, dacă e cunoscut."
          },
          "line_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numărul tău: pe care a intrat apelul (primit) sau de pe care a ieșit (dat)."
          },
          "line_number_national": {
            "type": [
              "string",
              "null"
            ],
            "description": "Același, național."
          },
          "agent_extension": {
            "type": [
              "string",
              "null"
            ],
            "description": "Interiorul colegului care a răspuns (primit) sau a format (dat). `null` pe un apel la care nu a vorbit nimeni, și când a răspuns asistentul AI sau mesageria vocală, nu un coleg."
          },
          "agent_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numele colegului, din CrioX."
          },
          "queue_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Coada de call-center prin care a trecut apelul, dacă a trecut printr-una."
          },
          "queue_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "result": {
            "type": "string",
            "enum": [
              "answered",
              "busy",
              "no_answer",
              "cancelled",
              "failed"
            ],
            "description": "Ce s-a întâmplat."
          },
          "missed_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "after_hours",
              "limit",
              "no_agent",
              "busy",
              "abandoned",
              "unanswered",
              null
            ],
            "description": "De ce nu a vorbit nimeni, doar pe un apel primit nepreluat."
          },
          "after_hours": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "A intrat în afara programului numărului. `null` = nu se aplică (apel dat) sau nu se știe."
          },
          "wait_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cât a așteptat apelantul până a vorbit cu cineva; pe un apel nepreluat, cât a sunat."
          },
          "talk_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cât s-a vorbit."
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "De la început la sfârșit."
          },
          "recorded": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Există înregistrare. Înregistrarea în sine nu se ia încă prin API."
          },
          "crm_provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "CRM-ul în care e cunoscut contactul (`kommo`, `istoma`, `medsoft`), dacă e."
          },
          "crm_contact_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id-ul fișei lui în acel CRM."
          },
          "crm_contact_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Legătura spre fișă."
          },
          "destination_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "extension",
              "group",
              "queue",
              "ivr",
              "assistant",
              "voicemail",
              "external",
              null
            ],
            "description": "Unde a trimis numărul apelul la intrare. Pe un apel dat, `external`. `null` când apelul s-a încheiat pe număr fără să fie trimis nicăieri."
          },
          "destination_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numărul destinației: interiorul, grupul (8001), coada (7001), meniul (500), asistentul (6001), căsuța vocală sau numărul din afară. `null` când centrala nu l-a consemnat."
          },
          "destination_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numele destinației, când se știe: al cozii, al meniului, al colegului."
          },
          "path": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/PathStep"
            },
            "description": "Traseul apelului, pas cu pas, în ordinea momentelor. `null` doar pe `call.answered`; listă goală când nu s-a consemnat niciun pas."
          }
        }
      },
      "PathStep": {
        "type": "object",
        "description": "Un pas din traseul apelului. Aceleași șapte chei pe orice pas.",
        "required": [
          "type",
          "number",
          "name",
          "key",
          "at",
          "seconds",
          "result"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "extension",
              "group",
              "queue",
              "ivr",
              "assistant",
              "voicemail",
              "external"
            ]
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numărul pasului: interiorul, coada, grupul, numărul din afară."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numele: al colegului, al cozii, al meniului; pe o tastă, ce înseamnă tasta."
          },
          "key": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tasta apăsată, doar pe un pas `ivr` cu `result: \"key\"`."
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "Când a început pasul, UTC."
          },
          "seconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cât a ținut pasul."
          },
          "result": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pe `ivr`: enter · key · exit. Pe `queue`: answered · abandoned · moved · timeout · no_agent · exit_key · other. Pe `extension` / `external`: answered · answered_elsewhere · no_answer · busy · offline · cancelled · rejected · failed. Pe `group` / `assistant` / `voicemail`: null."
          }
        }
      }
    }
  }
}
