Mediflo
REST API

Błędy

Koperta błędu, kody statusu HTTP i pełny katalog kodów zwracanych przez zewnętrzne API.

Koperta

Każdy błąd, bez wyjątku, ma ten sam kształt:

{
  "error": {
    "code": "API_KEY_INSUFFICIENT_SCOPE",
    "message": "Klucz API nie ma wymaganych uprawnień",
    "details": {
      "requiredScope": "tasks.write",
      "availableScopes": ["tasks.read", "users.read"]
    }
  }
}
PoleCo to jest
codeStabilny identyfikator maszynowy. Rozgałęziaj się na nim.
messageTekst dla człowieka. Tłumaczony na język wywołującego i może się zmienić bez zapowiedzi.
detailsOpcjonalny kontekst. Kształt zależy od kodu.

Nigdy nie parsuj `message`

message jest lokalizowany — ta sama sytuacja da inny tekst po polsku i po angielsku, a treść może się zmienić w każdym wydaniu. Jedyną częścią kontraktu jest code.

VALIDATION_ERROR

Wyjątek od reguły „details to obiekt": przy VALIDATION_ERROR details jest tablicą obiektów { field, message }, po jednym na każde pole, które nie przeszło walidacji.

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Walidacja nie powiodła się",
    "details": [
      { "field": "title", "message": "Tytuł jest wymagany" },
      { "field": "priority", "message": "Nieprawidłowa wartość. Oczekiwano 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT'" }
    ]
  }
}

field jest ścieżką z kropkami do miejsca w ciele żądania, np. context.variables.session_id.

Kody statusu HTTP

StatusZnaczenie
200 OKŻądanie się powiodło
201 CreatedZasób powstał
202 AcceptedPrzyjęte do przetworzenia asynchronicznego (zdarzenie testowe, ponowne dostarczenie)
400 Bad RequestŻądanie jest źle uformowane albo odrzucone przez usługę niżej
401 UnauthorizedBrak klucza, klucz błędny, wygasły albo unieważniony
403 ForbiddenKlucz poprawny, ale brakuje zakresu (albo polityka telefonii zabrania)
404 Not FoundZasób nie istnieje w Twoim workspace
409 ConflictZasób zmienił się pod żądaniem albo koliduje z istniejącym
410 GoneZasób istniał, ale jego treść jest już niedostępna
422 Unprocessable EntityWalidacja albo reguła biznesowa
429 Too Many RequestsLimit na klucz albo zajęta linia; patrz nagłówek Retry-After
500 Internal Server ErrorBłąd po naszej stronie
502 Bad GatewayUsługa, od której zależymy, padła lub była nieosiągalna
503 Service UnavailableNie udało się zweryfikować klucza z powodu awarii backendu. Ponawialne — poświadczenia są w porządku

404 na zasobie z innego workspace wygląda identycznie jak 404 na identyfikatorze, który nie istnieje. To celowe: API nie jest wyrocznią istnienia.

Katalog kodów

401 — uwierzytelnianie

KodZnaczenie
API_KEY_INVALIDFormat klucza jest zły albo klucza nie ma
API_KEY_EXPIREDKlucz minął datę ważności
API_KEY_REVOKEDKlucz został unieważniony

Poza rozróżnieniem „wygasł" / „unieważniony" 401 jest celowo nieinformacyjne — żeby nie dało się nim sondować, które klucze istnieją.

403 — uprawnienia

KodZnaczenie
API_KEY_INSUFFICIENT_SCOPEKlucz nie ma wymaganego zakresu; details niesie requiredScope i availableScopes
TELEPHONY_COUNTRY_NOT_ALLOWEDKraj docelowy jest poza tym, do czego ten workspace może dzwonić
BOT_WORKFLOW_SYSTEM_PROMPT_OVERRIDE_STAFF_ONLYWęzeł przepływu włącza overrideSystemPrompt, które może ustawić tylko zespół Mediflo; details.nodeIds wskazuje węzły

429 — limity

KodZnaczenie
RATE_LIMITEDPrzekroczony limit na klucz; details.retryAfterSeconds i nagłówek Retry-After
BOT_SESSION_LINE_BUSYNumer jest już wybierany albo bot osiągnął limit równoległych rozmów. Ponów z tym samym idempotencyKey; details.dispatchId to identyfikator, który zwróciłaby powtórka

Szczegóły limitów: Limity zapytań.

400 — żądanie odrzucone

KodZnaczenie
INVALID_REQUEST_DATACiało żądania nie przeszło walidacji schematu (trasy botów, przepływów, lokalizacji, wiedzy)
BOT_VALIDATION_ERRORDane bota są niespójne (np. lokalizacja spoza workspace)
BOT_CONFIG_INCOMPLETEPublikacja bez wymaganych pól; details.missingFields, a details.hint mówi, skąd wziąć brakujące
BOT_CONFIG_SCHEMA_TOO_COMPLEXremarksOutputSchemaJson przekracza dopuszczalną złożoność
BOT_CONFIG_PROCEDURES_INVALIDProcedury przypięte do konfiguracji nie kompilują się
BOT_ENTRY_PROCEDURE_INVALIDWskazana procedura wejściowa nie nadaje się na wejście
BOT_PROCEDURE_VALIDATION_FAILEDProcedura nie przeszła walidacji
BOT_PROCEDURE_REORDER_INCOMPLETEŻądanie zmiany kolejności nie wymienia wszystkich procedur
BOT_WORKFLOW_VALIDATION_FAILEDDefinicja przepływu jest nieprawidłowa; details niesie listę problemów
BOT_WORKFLOW_SKILL_REF_NOT_SERVABLEPrzepływ odwołuje się do umiejętności, której nie da się podać
PHONE_NUMBER_NOT_CONNECTED_TO_TRUNKNumer nie jest wpięty w trunk, więc nie da się podpiąć bota
TELEPHONY_INVALID_NUMBERDyspozytor uznał numer za niewybieralny
RESERVED_CONTEXT_VARIABLENazwa w context.variables jest zarezerwowana. W praktyce walidacja łapie to wcześniej jako 422 VALIDATION_ERROR
CONTACT_REQUEST_INVALID_CALL_REFERENCEtelephonyCallId nie istnieje w Twoim workspace
CONTACT_REQUEST_INVALID_DISPATCH_REFERENCEdispatcherDispatchId nie istnieje w Twoim workspace

404 — nie znaleziono

KodZnaczenie
TASK_NOT_FOUNDZadanie nie istnieje (albo zostało usunięte — patrz przegląd)
BOT_NOT_FOUNDBot nie istnieje w Twoim workspace
BOT_CONFIG_SNAPSHOT_NOT_FOUNDMigawka konfiguracji nie istnieje
BOT_KNOWLEDGE_SOURCE_NOT_FOUNDŹródło wiedzy nie istnieje
BOT_PROCEDURE_NOT_FOUNDProcedura nie istnieje
BOT_WORKFLOW_NOT_FOUNDPrzepływ nie istnieje
BOT_SESSION_NOT_FOUNDSesja bota nie istnieje
LOCATION_NOT_FOUNDLokalizacja nie istnieje
PHONE_NUMBER_NOT_FOUNDNumer nie istnieje
TICKET_NOT_FOUNDZgłoszenie nie istnieje
WEBHOOK_ENDPOINT_NOT_FOUNDPunkt końcowy webhooka nie istnieje albo został usunięty
WEBHOOK_DELIVERY_NOT_FOUNDDostarczenie nie istnieje

409 — konflikt

KodZnaczenie
BOT_CONFIG_CONFLICTexpectedUpdatedAt nie zgadza się z aktualnym stanem konfiguracji
BOT_CONFIG_PUBLISH_FAILEDPublikacja weszła w konflikt z równoległą zmianą
BOT_WORKFLOW_VERSION_CONFLICTexpectedVersion nie zgadza się z aktualną wersją przepływu
BOT_WORKFLOW_NAME_CONFLICTPrzepływ o tej nazwie już istnieje
BOT_WORKFLOW_ENTRY_PROCEDURE_CONFLICTPrzepływ i procedura wejściowa wykluczają się
BOT_PROCEDURE_NAME_CONFLICTProcedura o tej nazwie już istnieje
BOT_PROCEDURE_ENTRY_IN_USEProcedura wejściowa jest w użyciu i nie da się jej usunąć
BOT_PROCEDURE_LIMIT_EXCEEDEDBot ma już maksymalną liczbę procedur
LOCATION_CODE_EXISTSLokalizacja o tym kodzie już istnieje
PHONE_NUMBER_HAS_CUSTOM_FLOWNumer ma własny przepływ IVR; podpięcie bota nadpisałoby go
PHONE_NUMBER_MANAGED_BY_MEDIFLOWybrany numer to przekierowany numer placówki, którym zarządza Mediflo — nie może być numerem wychodzącym bota (POST /bots, PATCH /bots/{botId})
WEBHOOK_DELIVERY_NOT_REDELIVERABLEDostarczenie wciąż jest PENDING
BOT_SESSION_CREATE_FAILEDDyspozytor zgłosił identyfikator jako zajęty, ale sesji nie dało się jeszcze odczytać. Nic nowego nie zadzwoniło — ponów z tym samym idempotencyKey

410 — zasób wygasł

KodZnaczenie
WEBHOOK_DELIVERY_PAYLOAD_UNAVAILABLETreść dostarczenia nie jest już dostępna do odczytu

422 — walidacja i reguły biznesowe

KodZnaczenie
VALIDATION_ERRORCiało lub parametry nie przeszły walidacji; details to tablica { field, message }
OUTSIDE_CALL_WINDOWPoza oknem dzwonienia; details niesie timezone, window { startHour, endHour } i localTime
IDEMPOTENCY_KEY_REUSEDTen sam klucz idempotencji, inny bot lub numer; details.dispatchId to sesja, która ten klucz już trzyma
BOT_INACTIVEBot jest wyłączony w Portalu (wyłącznik operatora)
BOT_NO_OUTBOUND_NUMBERBot nie ma przypisanego numeru wychodzącego
BOT_NOT_PUBLISHEDBot nie ma opublikowanej konfiguracji
TICKET_INVALID_REPORTERreportedByUserId nie wskazuje aktywnego użytkownika Twojego workspace
TICKET_UNKNOWN_BOT_SESSIONKtóryś z botSessionIds nie istnieje; zgłoszenie nie powstało
WEBHOOK_ENDPOINT_INVALID_URLURL nie jest https albo rozwiązuje się na adres prywatny
WEBHOOK_ENDPOINT_LIMIT_REACHEDOsiągnięty limit punktów końcowych w workspace
WEBHOOK_ENDPOINT_DISABLEDPunkt końcowy jest wyłączony; włącz go przed testem
BOT_SESSION_CREATE_FAILEDOdmowa dyspozytora do naprawienia przez operatora (źle skonfigurowana pojemność, nieużywalny trunk); status z góry jest zachowany, a details niesie powód

502 — usługa niżej

KodZnaczenie
BOT_SESSION_CREATE_FAILEDDyspozytor padł albo był nieosiągalny. Żądanie mogło zostać przyjęte — ponów później z tym samym idempotencyKey
WEBHOOK_ENDPOINT_TEST_FAILEDNie udało się zakolejkować zdarzenia testowego

503 — chwilowa niedostępność

KodZnaczenie
EXTERNAL_API_UNAVAILABLENie udało nam się zweryfikować klucza z powodu awarii backendu

`503` nigdy nie oznacza problemu z kluczem

Nie rotuj poświadczeń w reakcji na 503. Ponów z wykładniczym odczekaniem.

500 — błąd po naszej stronie

Wszystkie znaczą to samo — operacja padła u nas — ale kod mówi, która. Ponów z wykładniczym odczekaniem; jeśli wraca, zgłoś to.

BOTS_FETCH_FAILED, BOT_FETCH_FAILED, BOT_CREATE_FAILED, BOT_UPDATE_FAILED, BOT_CONFIG_FETCH_FAILED, BOT_CONFIG_UPDATE_FAILED, BOT_CONFIG_PUBLISH_FAILED, BOT_CONFIG_ROLLBACK_FAILED, BOT_CONFIG_SNAPSHOTS_FETCH_FAILED, BOT_PROCEDURES_FETCH_FAILED, BOT_PROCEDURES_COMPILE_INVALID, BOT_PROCEDURE_FETCH_FAILED, BOT_PROCEDURE_CREATE_FAILED, BOT_PROCEDURE_UPDATE_FAILED, BOT_PROCEDURE_DELETE_FAILED, BOT_PROCEDURE_REORDER_FAILED, BOT_WORKFLOWS_FETCH_FAILED, BOT_WORKFLOW_FETCH_FAILED, BOT_WORKFLOW_CREATE_FAILED, BOT_WORKFLOW_UPDATE_FAILED, BOT_WORKFLOW_DELETE_FAILED, BOT_WORKFLOW_ACTIVATE_FAILED, BOT_WORKFLOW_DEACTIVATE_FAILED, BOT_WORKFLOW_VALIDATION_REQUEST_FAILED, BOT_SESSION_CREATE_FAILED, BOT_SESSION_TRANSCRIPT_FETCH_FAILED, EXTERNAL_API_BOT_SESSIONS_FETCH_FAILED, LOCATIONS_FETCH_FAILED, LOCATION_CREATE_FAILED, LOCATION_UPDATE_FAILED, PHONE_NUMBERS_FETCH_FAILED, PHONE_NUMBER_ATTACH_BOT_FAILED, MODEL_CATALOG_FETCH_FAILED, WEBHOOK_EVENT_TYPES_FETCH_FAILED, TASKS_FETCH_FAILED, TASK_CREATE_FAILED, TASK_UPDATE_FAILED, TASK_DELETE_FAILED, USERS_FETCH_FAILED, TAGS_FETCH_FAILED, EXTERNAL_API_CALLS_FETCH_FAILED, CONTACT_REQUESTS_FETCH_FAILED, CONTACT_REQUEST_CREATE_FAILED, TICKET_CREATE_FAILED, TICKET_UPDATE_FAILED, EXTERNAL_API_TICKETS_FETCH_FAILED, WEBHOOK_ENDPOINTS_FETCH_FAILED, WEBHOOK_ENDPOINT_CREATE_FAILED, WEBHOOK_ENDPOINT_UPDATE_FAILED, WEBHOOK_ENDPOINT_DELETE_FAILED, WEBHOOK_DELIVERIES_FETCH_FAILED, WEBHOOK_DELIVERY_REDELIVER_FAILED.

Obsługa błędów w kodzie

Ponawiaj 429 zawsze. 5xx ponawiaj tylko dla operacji bezpiecznych (GET, HEAD) albo takich, które sam zabezpieczyłeś kluczem idempotencji (np. POST /bot-sessions z idempotencyKey) — POST bez takiej gwarancji mógł się wykonać po stronie serwera, zanim dostałeś błąd, a ponowienie założy duplikat. Nie ponawiaj 4xx — one nie przejdą same z siebie.

async function medifloFetch(url, options = {}, { maxRetries = 3, idempotent = false } = {}) {
  const method = (options.method ?? 'GET').toUpperCase()
  const safeToRetry = idempotent || method === 'GET' || method === 'HEAD'

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options)
    if (response.ok) return response.json()

    const body = await response.json().catch(() => null)
    const code = body?.error?.code

    if (response.status === 429) {
      const retryAfter = Number(response.headers.get('Retry-After') ?? 1)
      await new Promise(r => setTimeout(r, retryAfter * 1000))
      continue
    }

    if (response.status >= 500 && safeToRetry) {
      await new Promise(r => setTimeout(r, 2 ** attempt * 1000))
      continue
    }

    // 4xx — rozgałęź się na kodzie, nie ponawiaj.
    switch (code) {
      case 'API_KEY_EXPIRED':
      case 'API_KEY_REVOKED':
        throw new Error('Zrotuj klucz API')
      case 'API_KEY_INSUFFICIENT_SCOPE':
        throw new Error(`Brakuje zakresu: ${body.error.details.requiredScope}`)
      case 'VALIDATION_ERROR':
        throw new Error(
          body.error.details.map(d => `${d.field}: ${d.message}`).join('; ')
        )
      default:
        throw new Error(`${response.status} ${code ?? 'UNKNOWN'}`)
    }
  }
  throw new Error('Wyczerpano ponowienia')
}

Przykład z curl, który pokazuje samą kopertę:

curl -s -o /dev/null -w '%{http_code}\n' \
  https://app.mediflo.ai/api/external/v1/bots \
  -H "Authorization: Bearer mflo_live_..."

Na tej stronie