Mediflo
Kolejki

Dynamiczne numery z API

Twój serwer decyduje, na jakie numery dzwoni kolejka zewnętrzna — osobno dla każdego oczekującego rozmówcy. Kontrakt, podpis, limity, tryb awaryjny i gotowe przykłady.

Zamiast stałej listy członków kolejka zewnętrzna może pytać Twój serwer, na jakie numery dzwonić. Gdy dzwoniący wchodzi do kolejki, Mediflo wysyła podpisane zapytanie z jego numerem, a Ty odpowiadasz listą numerów — na przykład jego stałego opiekuna, osoby z dzisiejszego dyżuru albo działu, który go obsługuje.

Tabele i przykłady na tej stronie są generowane z tego samego modułu, którym Mediflo buduje zapytanie i sprawdza odpowiedź. Wersja maszynowa, razem ze schematami JSON zapytania i odpowiedzi: /api/queue-routing.json.

Jak to działa

  1. Dzwoniący wchodzi do kolejki zewnętrznej ze źródłem numerów „Dynamicznie z API”.
  2. Mediflo wysyła jedno podpisane zapytanie POST na adres ustawiony w kolejce. Jedno zapytanie na jedno wejście do kolejki — nie na każde połączenie i nie co kilka sekund.
  3. Twój serwer odpowiada listą numerów. Kolejność na liście to kolejność wybierania.
  4. Mediflo sprawdza każdy numer (walidacja) i dzwoni po kolei na poprawne — na co najwyżej 10.
  5. Odbierający słyszy krótki komunikat z nazwą kolejki i numerem dzwoniącego: „wciśnij 1, aby przyjąć, 2, aby odrzucić”. Rozmowę przejmuje pierwsza osoba, która wciśnie 1.
  6. Gdy lista się skończy, a nikt nie przyjął, Mediflo po kilku sekundach zaczyna ją od początku — tę samą listę, bez ponownego pytania Twojego serwera (jeśli w całym przejściu żaden numer nawet nie zadzwonił, przerwa trwa 30 sekund — zob. Co się dzieje w trakcie oczekiwania). Tak aż do skutku: ktoś przyjmie, dzwoniący się rozłączy albo przepływ IVR wyprowadzi go z kolejki (np. po upływie czasu oczekiwania).

Jeśli Twój serwer nie odpowie poprawnie, kolejka przechodzi w tryb awaryjny wybrany w ustawieniach — dzwoniący nigdy nie zostaje bez obsługi tylko dlatego, że Twoje API nie działa.

Konfiguracja w Portalu

Funkcja na zamówienie

Numery z API włączamy dla organizacji na prośbę. Jeśli nie widzisz opcji „Dynamicznie z API”, skontaktuj się z Mediflo.

Konfigurację zmienia użytkownik z uprawnieniem do zarządzania kolejkami. Każda zmiana ustawień i każda rotacja sekretu trafia do dziennika audytu.

Otwórz ustawienia kolejki

W Portalu wejdź w Kolejki, wybierz kolejkę typu Zewnętrzna i przejdź do jej ustawień.

Zmień źródło numerów

W polu Źródło numerów wybierz Dynamicznie z API.

Podaj adres URL

Wpisz adres swojego endpointu. Wymagania: https://, adres publicznie osiągalny z internetu, bez loginu i hasła w adresie. Przekierowań (3xx) nie obsługujemy — podaj adres końcowy.

Wybierz tryb awaryjny

W polu Gdy API nie odpowie lub zwróci błąd zdecyduj, co się stanie, gdy Twój serwer nie odpowie poprawnie: Dzwoń do członków kolejki (według ich harmonogramów) albo Wyjdź z kolejki (przepływ IVR wyprowadza dzwoniącego z kolejki). Szczegóły w sekcji Tryb awaryjny. W sekcji Zaawansowane możesz też zmienić limit czasu odpowiedzi (domyślnie 3 s).

Zapisz i skopiuj sekret podpisu

Kliknij Zapisz. Po pierwszym zapisaniu Mediflo wygeneruje sekret podpisu w postaci whsec_… — skopiuj go (Pokaż, Kopiuj) do konfiguracji swojego serwera; posłuży do weryfikacji podpisu. To osobny sekret tej kolejki, nie sekret webhooków. Przycisk Wygeneruj nowy tworzy nowy sekret, a poprzedni podpisuje zapytania jeszcze przez 24 godziny.

Wyślij testowe zapytanie

Kliknij Wyślij testowe zapytanie. Portal pokaże status HTTP, czas odpowiedzi i każdy zwrócony numer z wynikiem walidacji. Nikt nie zostanie wybrany. W sekcji Ostatnie zapytania widać 20 ostatnich zapytań — testowych i prawdziwych.

Wyjście z kolejki wymaga gałęzi w przepływie IVR

Jeśli wybierasz tryb wyjście z kolejki, ustaw w węźle Kolejka przepływu IVR gałąź Przepełnienie albo Timeout. To nią dzwoniący opuści kolejkę, gdy Twój serwer nie odpowie.

Zapytanie

NagłówekZnaczenie
MetodaPOST na adres URL ustawiony w kolejce (tylko https://).
Content-Typeapplication/json
Mediflo-SignaturePodpis HMAC-SHA256: t=<unix>,v1=<hex>. W okresie rotacji sekretu dwa wpisy v1.
Mediflo-Event-IdIdentyfikator zapytania (evt_…), taki sam jak pole id.
Mediflo-Event-TypeZawsze queue.numbers.requested.
Mediflo-Event-Testtrue tylko dla zapytania testowego z Portalu. Nikt nie czeka wtedy na połączenie.
User-AgentMediflo-Queue-Routing/1

Przykładowe ciało:

{  "id": "evt_7f1c2d4e-8a9b-4c3d-9e0f-1a2b3c4d5e6f",  "type": "queue.numbers.requested",  "apiVersion": "v1",  "occurredAt": "2026-10-01T08:15:02.120Z",  "data": {    "queue": {      "id": "3b0f6c2e-5a4d-4f1e-9c8b-7a6d5e4f3c2b",      "name": "Rejestracja"    },    "call": {      "id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",      "calledNumber": "+48221234567",      "enteredQueueAt": "2026-10-01T08:15:01.950Z"    },    "caller": {      "phoneNumber": "+48600111222"    }  }}
PoleTypZnaczenie
idstringIdentyfikator zapytania (evt_<uuid>). Ta sama wartość jest w nagłówku Mediflo-Event-Id.
typestringZawsze queue.numbers.requested.
apiVersionstringWersja kontraktu, obecnie v1.
occurredAtstring (ISO 8601, UTC)Moment utworzenia zapytania.
data.queue.idstring (UUID)Identyfikator kolejki w Mediflo.
data.queue.namestringNazwa kolejki z Portalu.
data.call.idstring (UUID)Identyfikator rozmowy w Mediflo.
data.call.calledNumberstring | nullNumer, pod który zadzwoniono (E.164), albo null, gdy nie jest znany.
data.call.enteredQueueAtstring (ISO 8601, UTC)Moment wejścia dzwoniącego do kolejki.
data.caller.phoneNumberstring | nullNumer dzwoniącego (E.164) albo null, gdy numer jest zastrzeżony.

O dzwoniącym wysyłamy wyłącznie numer telefonu. Zapytanie nie zawiera imienia i nazwiska, identyfikatora kontaktu ani żadnych innych jego danych osobowych. Jeśli potrzebujesz więcej, wyszukaj numer we własnym systemie.

Pole id (i nagłówek Mediflo-Event-Id) ma tę samą wartość także przy ponowieniu — możesz po nim rozpoznać, że to samo zapytanie przyszło drugi raz. Parsuj pobłażliwie: ignoruj pola, których nie znasz.

Weryfikacja podpisu

Podpis działa dokładnie tak samo jak w webhookach — jeśli masz już odbiornik webhooków Mediflo, użyj tej samej funkcji, tylko z sekretem kolejki. Pełny opis: Weryfikacja podpisu webhooków.

Mediflo-Signature: t=<sekundy-unix>,v1=<hex>[,v1=<hex>]
v1 = HMAC_SHA256(sekret, "<t>.<surowe ciało>")   → zapis szesnastkowy
  • Liczysz HMAC z surowych bajtów ciała, zanim cokolwiek je sparsuje. Ponowna serializacja JSON-a nie odtworzy tego samego podpisu.
  • Odrzucaj zapytania, których t odbiega od Twojego zegara o więcej niż ±300 s.
  • Przez 24 godziny po rotacji sekretu nagłówek ma dwa wpisy v1= — najpierw dla nowego sekretu, potem dla poprzedniego. Zaakceptuj zapytanie, jeśli pasuje którykolwiek.
  • Porównuj podpisy w stałym czasie (timingSafeEqual, hash_equals, hmac.compare_digest), nigdy zwykłym ==.
  • Zapytanie z błędnym podpisem odrzuć statusem 401. Mediflo potraktuje to jak błąd i zastosuje tryb awaryjny.

Gotowe funkcje weryfikujące — te same co dla webhooków:

import { createHmac, timingSafeEqual } from 'node:crypto'/** * Verify a Mediflo webhook signature. * * Header shape: `t=<unix-seconds>,v1=<hex>[,v1=<hex>]` * where `v1 = HMAC_SHA256(secret, "<t>.<rawBody>")`. * * `rawBody` MUST be the exact bytes we sent. Re-serializing parsed JSON does * not reproduce the digest — in Express, use * `express.json({ verify: (req, _res, buf) => { req.rawBody = buf } })`. * * Pass BOTH secrets during a rotation window (or just the current one; the * header carries a `v1=` entry per active secret and any match is valid). */export function verifyMedifloSignature(  header: string | undefined,  rawBody: string | Buffer,  secrets: string[],  toleranceSeconds = 300): boolean {  if (!header || secrets.length === 0) return false  let timestamp: number | null = null  const presented: string[] = []  for (const part of header.split(',')) {    const trimmed = part.trim()    const eq = trimmed.indexOf('=')    if (eq <= 0) continue    const key = trimmed.slice(0, eq)    const value = trimmed.slice(eq + 1)    if (key === 't') {      if (!/^\d+$/.test(value)) return false      timestamp = Number(value)    } else if (key === 'v1') {      // More than one entry appears during a 24 h secret rotation.      presented.push(value)    }  }  if (timestamp === null || presented.length === 0) return false  // Replay protection: the timestamp is INSIDE the signed string, so an  // attacker cannot move it forward without invalidating every digest.  const now = Math.floor(Date.now() / 1000)  if (Math.abs(now - timestamp) > toleranceSeconds) return false  for (const secret of secrets) {    const expected = createHmac('sha256', secret)      .update(`${timestamp}.${rawBody}`)      .digest('hex')    for (const candidate of presented) {      // Validate the hex BEFORE comparing. Buffer.from('zz', 'hex') truncates      // silently, and timingSafeEqual throws RangeError on a length mismatch —      // so without these two guards a malformed header crashes the handler      // instead of returning false.      const normalized = candidate.trim().toLowerCase()      if (!/^[0-9a-f]+$/.test(normalized)) continue      if (normalized.length !== expected.length) continue      if (        timingSafeEqual(          Buffer.from(normalized, 'hex'),          Buffer.from(expected, 'hex')        )      ) {        return true      }    }  }  return false}

Odpowiedź

Odpowiedz statusem 2xx i ciałem JSON (zalecany nagłówek Content-Type: application/json):

{  "numbers": [    {      "phoneNumber": "+48600100200",      "label": "dr Anna Nowak"    },    "+48600100300"  ]}
  • numbers to tablica. Każda pozycja to napis z numerem albo obiekt { "phoneNumber": "…", "label": "…" } — label jest opcjonalny. Oba kształty można mieszać.
  • Kolejność = kolejność wybierania. Pierwszy numer dzwoni pierwszy.
  • Mediflo wybiera najwyżej 10 numerów — pierwsze poprawne z listy. Lista może mieć do 50 pozycji; dłuższa jest w całości odrzucana jako niepoprawna.
  • label ma najwyżej 80 znaków (dłuższy przycinamy). Pokazujemy go w historii połączeń w Portalu, np. „Anna Nowak” zamiast samego numeru.
  • Zalecany format numeru to E.164 (+48600100200). Format krajowy (600 100 200) też zadziała — interpretujemy go według kraju Twojej organizacji.
  • Nieznane pola ignorujemy, zarówno w głównym obiekcie, jak i w pozycjach listy.
  • Pusta lista oznacza „brak numerów” i uruchamia tryb awaryjny (no_valid_numbers).
  • Niepoprawna pozycja (np. liczba zamiast napisu) odpada sama (invalid_entry); pozostałe są przetwarzane normalnie.
LimitWartość
Limit czasu na odpowiedź3 s domyślnie; w Portalu od 1 s do 5 s
Maksymalny rozmiar odpowiedzi16 KiB
Maksymalna liczba pozycji w numbers50
Maksymalna liczba wybieranych numerów10 (pierwsze poprawne, w kolejności z listy)
Maksymalna długość label80 znaków (dłuższa jest przycinana)

Walidacja numerów

Każdy zwrócony numer jest sprawdzany osobno. Odrzucony numer nie blokuje pozostałych — Mediflo po prostu go pomija. Wynik dla każdego numeru zobaczysz po wysłaniu testowego zapytania i na liście ostatnich zapytań w Portalu.

PowódZnaczenie
invalid_entryPozycja nie jest napisem ani obiektem z polem tekstowym phoneNumber.
invalid_numberTo nie jest poprawny numer telefonu.
country_unresolvedNie udało się ustalić kraju numeru.
country_not_allowedKraj numeru nie jest dozwolony w ustawieniach połączeń organizacji.
premium_rateNumer o podwyższonej opłacie.
mediflo_numberNumer obsługiwany przez Mediflo (np. numer kliniki przekierowany do Mediflo). Połączenie wróciłoby do kolejki.
caller_numberNumer osoby dzwoniącej.
duplicateNumer powtórzony na liście (liczy się pierwsze wystąpienie).
over_limitNumer poza limitem 10 poprawnych numerów.

Limit 10 dotyczy numerów, które przeszły walidację: jeśli trzy pierwsze pozycje odpadną, wybierane są kolejne poprawne, aż do dziesięciu.

Limity czasu, błędy i tryb awaryjny

Limity czasu i ponowienie

Twój serwer ma domyślnie 3 sekundy na pełną odpowiedź — liczone od rozpoczęcia połączenia (razem z DNS i TLS) do ostatniego bajtu. W Portalu możesz ustawić od 1 do 5 sekund. Dzwoniący czeka w tym czasie w kolejce, więc odpowiadaj tak szybko, jak to możliwe; celuj w mniej niż sekundę.

Mediflo ponawia zapytanie najwyżej raz i tylko wtedy, gdy:

  • pierwsza próba zakończyła się błędem sieci, statusem 5xx albo 429,
  • stało się to szybko (w mniej niż połowie limitu czasu) i zostało co najmniej 0,5 s.

Ponowienie mieści się w tym samym limicie czasu i ma to samo id. Po przekroczeniu czasu, statusie 4xx (poza 429) albo niepoprawnej odpowiedzi ponowienia nie ma.

Kody błędów

Każdy z tych przypadków oznacza, że dla tego wejścia do kolejki obowiązuje tryb awaryjny. Kod błędu widać w wyniku testowego zapytania i na liście ostatnich zapytań w Portalu.

Kod błęduZnaczenie
not_configuredKolejka nie ma ustawionego adresu URL lub sekretu podpisu.
secret_unavailableNie udało się odczytać sekretu podpisu kolejki.
invalid_urlAdres URL jest nieprawidłowy (wymagany https, bez loginu i hasła w adresie).
host_not_allowedNie można połączyć się z hostem: nazwa nie istnieje albo wskazuje na sieć prywatną lub zastrzeżoną.
timeoutBrak pełnej odpowiedzi w ustawionym czasie.
network_errorBłąd połączenia (np. odrzucone połączenie lub błąd TLS).
redirectSerwer odpowiedział przekierowaniem (3xx). Przekierowania nie są obsługiwane.
http_statusSerwer zwrócił status HTTP spoza zakresu 2xx.
response_too_largeOdpowiedź przekroczyła 16 KiB.
invalid_jsonOdpowiedź nie jest poprawnym JSON-em.
invalid_bodyJSON nie jest obiektem z tablicą numbers (maksymalnie 50 pozycji).
no_valid_numbersŻaden zwrócony numer nie przeszedł walidacji.
circuit_openKilka zapytań z rzędu zakończyło się błędem, więc przez 60 sekund Mediflo nie wysyła zapytań i od razu stosuje tryb awaryjny.
interruptedZapytanie zostało przerwane po stronie Mediflo.

Przerwa po serii błędów

Jeśli 5 zapytań z rzędu dla danej kolejki zakończy się błędem serwera lub połączenia (np. timeout, http_status, invalid_json), Mediflo przez 60 sekund w ogóle nie pyta Twojego serwera i od razu stosuje tryb awaryjny (circuit_open). Po tej przerwie Mediflo znów wysyła zapytania: poprawna odpowiedź przywraca normalne działanie, a kolejny błąd od razu wznawia przerwę na 60 sekund. Poprawna odpowiedź bez użytecznych numerów (np. pusta lista, no_valid_numbers) nie jest błędem serwera — przerywa serię. Zapytania testowe z Portalu nie liczą się do tej serii.

Tryb awaryjny

Tryb awaryjny dotyczy jednego wejścia do kolejki. Następny dzwoniący znów wywoła zapytanie (chyba że trwa przerwa po serii błędów).

TrybCo się dzieje
Członkowie kolejkiMediflo dzwoni do członków kolejki według ich harmonogramów — tak jak w zwykłej kolejce zewnętrznej.
Wyjście z kolejkiMediflo nikogo nie wybiera. Przepływ IVR wyprowadza dzwoniącego gałęzią Przepełnienie węzła Kolejka, a jeśli jej nie ma — gałęzią Timeout.

Zabezpieczenie przed czekaniem bez końca

Jeśli w trybie wyjście z kolejki węzeł nie ma ani gałęzi Przepełnienie, ani Timeout (albo dzwoniący trafił do kolejki inaczej niż przez przepływ IVR), po 30 sekundach Mediflo i tak zacznie dzwonić do członków kolejki.

Co się dzieje w trakcie oczekiwania

  • Pętla tej samej listy. Po przejściu całej listy bez przyjęcia Mediflo zaczyna od początku, z tymi samymi numerami. Listy nie da się zmienić w trakcie oczekiwania — nowe numery zadziałają dopiero przy następnym wejściu do kolejki. Jeśli w całym przejściu żaden numer nawet nie zadzwonił, przed kolejnym Mediflo odczekuje 30 sekund.
  • Odrzucenie jest zapamiętywane. Numer, który wcisnął 2 dla tego dzwoniącego, nie dostanie już kolejnego połączenia w jego sprawie.
  • Zajęte numery są pomijane. Numer, pod który właśnie dzwonimy w sprawie innego dzwoniącego, albo numer w trakcie rozmowy przyjętej z kolejki zewnętrznej jest pomijany i próbowany ponownie w następnym przejściu.
  • Jeden numer, wielu dzwoniących. Możesz zwrócić ten sam numer dla wielu oczekujących — Mediflo zaproponuje go zawsze tylko jednemu dzwoniącemu naraz.
  • Limit połączeń. Organizacja ma jednocześnie najwyżej 10 takich połączeń wychodzących. Pozostali dzwoniący czekają na swoją kolej.
  • Rozłączenie dzwoniącego kończy wszystko: trwające połączenie wychodzące jest przerywane, a kolejne nie są wykonywane.
  • Nowe wejście = nowe zapytanie. Gdy dzwoniący zadzwoni ponownie albo zostanie przeniesiony do innej kolejki z numerami z API, Mediflo wyśle nowe zapytanie.
  • Przypisanie rozmowy. Jeśli przyjmujący numer to telefon zewnętrzny z profilu użytkownika Portalu, rozmowa zostanie przypisana do tego użytkownika. W przeciwnym razie w historii połączeń zobaczysz numer i jego label.

Przypadki brzegowe

Dzwoniący zastrzegł numer

data.caller.phoneNumber ma wtedy wartość null. Zwróć numery, które obsłużą każdego dzwoniącego — na przykład ogólny numer biura obsługi klienta.

Numer organizacji przekierowany do Mediflo

Takiego numeru nie wybierzemy (mediflo_number): połączenie wróciłoby do Mediflo, a często do tej samej kolejki. To samo dotyczy każdego numeru obsługiwanego przez Mediflo. Zwracaj numery, które dzwonią bezpośrednio u konsultanta — komórkę albo numer wewnętrzny centrali dostępny z zewnątrz.

Numer samego dzwoniącego

Jest odrzucany (caller_number) — nie oddzwaniamy do dzwoniącego z jego własnej kolejki.

Numery zagraniczne

Kraj numeru musi być dozwolony w ustawieniach połączeń Twojej organizacji (country_not_allowed). Jeśli potrzebujesz dzwonić za granicę, skontaktuj się z Mediflo.

Numery o podwyższonej opłacie

Są zawsze blokowane (premium_rate).

Węzeł „Sprawdzenie kolejki” w przepływie IVR

Ten węzeł sprawdza wyłącznie konsultantów zalogowanych do Portalu. Nie wie nic o numerach, które zwróci Twój serwer, więc przed kolejką z numerami z API może skierować dzwoniącego na ścieżkę „niedostępny”, mimo że ktoś pod Twoimi numerami czeka. Z tego samego powodu w kolejce z numerami z API gałąź Przepełnienie węzła Kolejka nie reaguje na brak zalogowanych konsultantów — służy tylko jako wyjście w trybie awaryjnym.

Jak wygląda zapytanie testowe

Ma nagłówek Mediflo-Event-Test: true, prawdziwy podpis, losowy identyfikator rozmowy i przykładowy numer dzwoniącego. Odpowiadaj na nie tak samo jak na prawdziwe — Portal pokaże, jak Mediflo oceni każdy zwrócony numer. Nikt nie zostanie wybrany, a za zapytaniem testowym nie stoi żaden prawdziwy dzwoniący, więc nie zapisuj go jako prawdziwej rozmowy.

Kto przyjął rozmowę?

W historii połączeń w Portalu widać, który numer przyjął rozmowę i z jaką etykietą (label).

Przykładowa implementacja

Minimalny serwer: weryfikuje podpis z surowych bajtów, sprawdza znacznik czasu, akceptuje dowolny z wpisów v1= i zwraca listę numerów. Sekret (i przez 24 h po rotacji — poprzedni) trzymaj w zmiennych środowiskowych.

import express from 'express'
import { createHmac, timingSafeEqual } from 'node:crypto'

// Bieżący sekret i — przez 24 h po rotacji — poprzedni.
const SECRETS = [process.env.MEDIFLO_QUEUE_SECRET, process.env.MEDIFLO_QUEUE_SECRET_PREVIOUS].filter(Boolean)
const TOLERANCE_SECONDS = 300

function verifySignature(header, rawBody, secrets) {
  if (!header || secrets.length === 0) return false
  let timestamp = null
  const presented = []
  for (const part of header.split(',')) {
    const [key, ...rest] = part.trim().split('=')
    const value = rest.join('=')
    if (key === 't') {
      if (!/^\d+$/.test(value)) return false
      timestamp = Number(value)
    } else if (key === 'v1') {
      presented.push(value.trim().toLowerCase())
    }
  }
  if (timestamp === null || presented.length === 0) return false
  if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false

  for (const secret of secrets) {
    const expected = createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest('hex')
    for (const candidate of presented) {
      // Sprawdź format przed porównaniem: timingSafeEqual rzuca wyjątek przy różnej długości.
      if (!/^[0-9a-f]{64}$/.test(candidate)) continue
      if (timingSafeEqual(Buffer.from(candidate, 'hex'), Buffer.from(expected, 'hex'))) return true
    }
  }
  return false
}

// Twoja logika: grafik, dyżur, stały opiekun dzwoniącego…
function pickNumbers({ queue, caller }) {
  return [{ phoneNumber: '+48600100200', label: 'Anna Nowak' }, '+48600100300']
}

const app = express()

// express.raw: podpis liczymy z SUROWYCH bajtów, przed parsowaniem JSON-a.
app.post('/mediflo/queue-numbers', express.raw({ type: 'application/json', limit: '64kb' }), (req, res) => {
  if (!Buffer.isBuffer(req.body) || !verifySignature(req.get('Mediflo-Signature'), req.body, SECRETS)) {
    return res.status(401).end()
  }
  const event = JSON.parse(req.body.toString('utf8'))
  if (event.type !== 'queue.numbers.requested') return res.status(400).end()

  const isTest = req.get('Mediflo-Event-Test') === 'true'
  console.log(`zapytanie ${event.id}${isTest ? ' (test)' : ''}, kolejka ${event.data.queue.name}`)

  res.json({ numbers: pickNumbers(event.data) })
})

app.listen(3000)

Test lokalny z curl

Skrypt podpisuje przykładowe zapytanie tym samym wzorem co Mediflo i wysyła je na lokalny serwer z przykładu powyżej. Wstaw ten sam sekret, którego używa serwer: poprawny serwer odpowie 200 i listą numerów, a przy innym sekrecie — 401.

# Sekret skopiowany z ustawień kolejki w Portalu.SECRET='whsec_…'# Dokładnie te bajty są podpisywane i wysyłane (bez końcowego znaku nowej linii).BODY='{"id":"evt_7f1c2d4e-8a9b-4c3d-9e0f-1a2b3c4d5e6f","type":"queue.numbers.requested","apiVersion":"v1","occurredAt":"2026-10-01T08:15:02.120Z","data":{"queue":{"id":"3b0f6c2e-5a4d-4f1e-9c8b-7a6d5e4f3c2b","name":"Rejestracja"},"call":{"id":"9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a","calledNumber":"+48221234567","enteredQueueAt":"2026-10-01T08:15:01.950Z"},"caller":{"phoneNumber":"+48600111222"}}}'T=$(date +%s)SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')curl -sS -i -X POST http://localhost:3000/mediflo/queue-numbers \  -H 'Content-Type: application/json' \  -H "Mediflo-Signature: t=$T,v1=$SIG" \  -H 'Mediflo-Event-Id: evt_7f1c2d4e-8a9b-4c3d-9e0f-1a2b3c4d5e6f' \  -H 'Mediflo-Event-Type: queue.numbers.requested' \  -H 'Mediflo-Event-Test: true' \  --data-binary "$BODY"

Lista kontrolna przed uruchomieniem

  • Endpoint działa pod publicznym adresem https://, bez przekierowań i bez loginu ani hasła w adresie.
  • Podpis jest weryfikowany z surowych bajtów, z tolerancją ±300 s, i akceptuje dowolny z wpisów v1=.
  • Sekret kolejki leży w konfiguracji serwera (nie w kodzie ani w logach); wiesz, jak podmienić go po rotacji.
  • Odpowiedź przychodzi wyraźnie przed limitem czasu (celuj w mniej niż sekundę), także przy obciążeniu.
  • Zwracasz numery w kolejności wybierania, najlepiej w formacie E.164, z czytelnym label.
  • Nie zwracasz numerów, które prowadzą z powrotem do Mediflo (np. numeru organizacji przekierowanego do Mediflo).
  • Masz odpowiedź dla zastrzeżonego numeru (phoneNumber: null).
  • Wybrany tryb awaryjny pasuje do Twojej organizacji; przy „wyjściu z kolejki” węzeł Kolejka ma gałąź Przepełnienie lub Timeout.
  • Przed kolejką nie stoi węzeł „Sprawdzenie kolejki”, który odeśle dzwoniących, bo nikt nie jest zalogowany do Portalu.
  • Testowe zapytanie z Portalu kończy się sukcesem, a każdy numer ma oczekiwany wynik walidacji.
  • Nie logujesz pełnych numerów dzwoniących dłużej, niż to konieczne — to dane osobowe.

Na tej stronie