Finestre temporali di accesso
Le Access Time Windows consentono a un Partner Member di avere un accesso temporaneo o ricorrente fornito da un sistema esterno. Questa pagina è la guida autorevole per i produttori di access_time_windows e access_time_windows_revision.
Le Access Time Windows sono autorizzazioni aggiuntive che possono solo consentire l'accesso. Una finestra attiva può consentire l'accesso, ma una finestra inattiva, scaduta, obsoleta o non mappata non nega un accesso già consentito da una regola ordinaria o da una concessione permanente. Restano comunque validi la disattivazione dell'utente, il blocco esplicito delle porte, la modalità di manutenzione, il geofencing, i limiti di frequenza e la disponibilità del controller.
Le implementazioni per specifici sistemi a monte sono adattatori separati. Devono convertire i propri dati di origine in questo contratto indipendente dal fornitore, senza modificare il significato dei campi.
Campi dell'istantanea del membro
-
access_time_windows
arrayfacoltativoL'istantanea completa delle Access Time Windows per questo membro e questa fonte partner. Un'istantanea può combinare finestre
absoluteeweekly. Ometti il campo per lasciare invariata l'istantanea salvata; invia[]per revocare tutte le finestre di proprietà di questa fonte. -
access_time_windows_revision
integerobbligatorio con access_time_windowsUn intero non negativo, sicuro per JSON e monotonicamente crescente per questo flusso di revisioni membro/fonte. È obbligatorio ogni volta che è presente
access_time_windowse non deve superare9007199254740991.
Ogni istantanea accetta al massimo 50 finestre. external_id, scope_key e label accettano al massimo 120 caratteri. Le finestre assolute possono coprire al massimo 90 giorni. Un giorno settimanale accetta al massimo otto blocchi non sovrapposti. I dati non validi vengono rifiutati, anziché troncati o interpretati per ipotesi.
Forme delle finestre
Una finestra assoluta identifica un unico intervallo esatto:
{
"external_id": "access-4b1782",
"kind": "absolute",
"from": 1792053000,
"to": 1792058400,
"timezone": "Australia/Perth",
"scope_key": "after_hours_entry",
"label": "Temporary site access"
}
I campi obbligatori sono external_id, kind, from e to. timezone, scope_key e label sono facoltativi.
Una finestra settimanale identifica intervalli ricorrenti in ora locale, eventualmente delimitati da istanti UTC:
{
"external_id": "weekly-early-access",
"kind": "weekly",
"timezone": "America/New_York",
"access_hours": {
"sun": [],
"mon": [["06:00", "08:30"]],
"tue": [["06:00", "08:30"]],
"wed": [["06:00", "08:30"]],
"thu": [["06:00", "08:30"]],
"fri": [["06:00", "08:30"]],
"sat": []
},
"active_from": 1790812800,
"active_until": 1796083200,
"scope_key": "main_entry",
"label": "Weekday early access"
}
I campi obbligatori sono external_id, kind e access_hours. timezone, active_from, active_until, scope_key e label sono facoltativi.
Richiesta mista completa
Questa richiesta sostituisce la precedente istantanea di proprietà della fonte per il membro con una finestra assoluta e una settimanale:
curl -X POST https://partner-api.performancehub.co/partner/v1/members \
-u sk_live_YOUR_PARTNER_SECRET_KEY: \
-H "Content-Type: application/json" \
-d '{
"members": [
{
"member_id": "member_11111",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "John",
"last_name": "Smith",
"access_time_windows_revision": 1842,
"access_time_windows": [
{
"external_id": "access-4b1782",
"kind": "absolute",
"from": 1792053000,
"to": 1792058400,
"timezone": "Australia/Perth",
"scope_key": "after_hours_entry",
"label": "Temporary site access"
},
{
"external_id": "weekly-early-access",
"kind": "weekly",
"timezone": "America/New_York",
"access_hours": {
"sun": [],
"mon": [["06:00", "08:30"]],
"tue": [["06:00", "08:30"]],
"wed": [["06:00", "08:30"]],
"thu": [["06:00", "08:30"]],
"fri": [["06:00", "08:30"]],
"sat": []
},
"active_from": 1790812800,
"active_until": 1796083200,
"scope_key": "main_entry",
"label": "Weekday early access"
}
]
}
]
}'
Usa facility_id oppure linking_identifier in base al metodo di collegamento configurato. Non inviarli entrambi.
Regole su orari e limiti
from, to, active_from e active_until sono secondi Unix espressi come interi JSON che identificano istanti UTC:
- Invia un intero JSON senza virgolette. Stringhe numeriche, numeri in virgola mobile, stringhe RFC 3339 e millisecondi Unix vengono rifiutati.
- Ogni intervallo è semiaperto:
[from, to). L'accesso può iniziare esattamente afrome non è più attivo esattamente ato. fromdeve essere minore dito. Quando sono presenti entrambi i limiti settimanali,active_fromdeve essere minore diactive_until.- Non ricavare gli epoch da una data/ora locale senza fuso orario. Prima converti l'orario di origine in un istante UTC, poi dividi i millisecondi Unix per 1000 e verifica che il risultato sia un intero.
Conversione in epoch
2026-10-15T08:30:00Z diventa 1792053000:
UTC instant Unix milliseconds Unix seconds sent
2026-10-15T08:30:00Z 1792053000000 1792053000
Conversione indipendente dal linguaggio:
instant = parse_source_timestamp_with_explicit_zone(source_timestamp)
milliseconds = instant.to_unix_milliseconds()
assert milliseconds modulo 1000 equals 0
seconds = milliseconds / 1000
assert seconds is a JSON-safe nonnegative integer
Se la fonte ha una precisione inferiore al secondo, definisci una regola di arrotondamento intenzionale per inizio e fine prima dei test di integrazione. Non passare mai millisecondi affidandoti all'API per dedurre l'unità.
Fusi orari e ora legale
timezone deve essere un fuso orario IANA valido, come UTC, Australia/Perth o America/New_York. Se omesso, il valore predefinito è UTC.
- Finestre assolute:
frometoidentificano già istanti UTC esatti.timezoneè solo un metadato di visualizzazione e audit; modificarlo non sposta mai nessuno dei due istanti. - Finestre settimanali:
timezonedefinisce l'ora locale e le regole dell'ora legale usate per valutareaccess_hours. Non convertire in anticipo i blocchi settimanali in UTC. - Durante il passaggio all'ora legale, un'ora locale saltata dal fuso orario non si verifica mai.
- Durante il ritorno all'ora solare, entrambi gli istanti reali che corrispondono all'ora locale ripetuta rientrano nel blocco settimanale.
active_fromeactive_untilrestano istanti UTC anche quando una finestra settimanale usa un fuso orario diverso da UTC.
access_hours usa le chiavi minuscole da sun a sat. Ogni valore è un array di coppie [start, end] nel formato HH:mm a 24 ore con zeri iniziali. I blocchi devono essere non vuoti, ordinati e non sovrapposti. end è escluso. Un blocco non può attraversare la mezzanotte: suddividi un intervallo notturno di origine in un blocco serale il primo giorno e un blocco mattutino il giorno successivo.
ID stabili e mappatura degli ambiti
external_id deve restare stabile per tutta la durata di una finestra logica ed essere univoco all'interno dell'istantanea del membro. Mantienilo stabile quando cambiano orari o etichette, in modo che operatori e record di audit possano correlare le revisioni. Non riutilizzarlo per una finestra non correlata.
scope_key è la tua destinazione di accesso logica stabile. Il valore predefinito è default. Un amministratore della struttura mappa ogni chiave consentita a destinazioni del Performance Hub valide per l'intera struttura, per una porta o per un Ambito di accesso:
Partner scope_key Performance Hub mapping
default Facility's configured default target
main_entry Access Scope "Main entrances"
after_hours_entry Doors approved for after-hours entry
I partner non inviano ID di strutture, porte o Ambiti di accesso del Performance Hub all'interno di una finestra. Uno scope_key sconosciuto, disattivato o non mappato nega l'accesso per quella finestra; non amplia l'accesso. Concorda e testa le mappature prima del lancio.
Istantanee complete e revisioni
Tratta i due campi come una partizione con versioni separate all'interno dell'upsert del Member:
| Input | Risultato |
|---|---|
access_time_windows omesso | L'istantanea esistente resta invariata; non è richiesta alcuna revisione |
| Presente con una revisione più recente | L'intera istantanea salvata per questo membro/fonte viene sostituita |
| Stessa revisione e stesso payload normalizzato | Ripetizione idempotente; l'istantanea non cambia |
| Stessa revisione e payload normalizzato diverso | Conflitto; assegna una nuova revisione dopo la riconciliazione |
| Revisione più vecchia | L'istantanea obsoleta viene saltata; non può sovrascrivere uno stato più recente |
access_time_windows: [] con una revisione più recente | Revoca esplicitamente tutte le finestre di proprietà della fonte |
Un'istantanea è completa, non una modifica parziale. Se la revisione 1842 contiene le finestre A e B, la revisione 1843 che contiene solo B rimuove A.
Mantieni un unico flusso di revisioni coordinato per partner/membro tra worker dei webhook, polling, ripetizioni e riconciliazione completa. È preferibile una sequenza persistente in un database. Un valore in millisecondi Unix può essere usato come revisione solo se tutti i writer si coordinano e garantiscono ordinamento e univocità rigorosi; si tratta di un valore di revisione, non di un campo epoch.
Conserva la richiesta e la revisione esatte finché non conosci la risposta dell'API. Dopo un timeout o un errore temporaneo, riprova con la stessa istantanea normalizzata e la stessa revisione. Non assegnare una nuova revisione solo perché la consegna è incerta.
Annullamento, scadenza ed errori a monte
- Annullare una finestra: recupera correttamente lo stato autorevole dalla fonte, rimuovi quella finestra e invia l'istantanea completa rimanente con una revisione più recente.
- Annullare tutte le finestre: invia
access_time_windows: []con una revisione più recente. - Lasciare scadere una finestra assoluta: smette di consentire l'accesso a
to; invia una riconciliazione successiva senza la voce scaduta per mantenere pulite le istantanee. - Terminare una finestra settimanale: rimuovila in un'istantanea più recente oppure usa
active_untilquando la fonte ha un istante di fine noto. - Il recupero dalla fonte non riesce o è incompleto: non inviare
[]e non pubblicare un'istantanea parziale. Invia un avviso e riprova il recupero. Trasformare un errore a monte in un'istantanea vuota significa revocare l'accesso involontariamente. - La richiesta alla Partner API non supera la convalida: correggi i dati e invia una revisione più recente solo se l'istantanea normalizzata cambia. Riprova gli errori temporanei con la revisione originale.
L'aggiornamento dei dati di origine si configura per ogni integrazione in base alla frequenza di consegna e di riconciliazione promessa. Prima dell'attivazione, concorda l'età massima dei dati di origine, cosa succede quando viene superata, le soglie di avviso e se gli operatori possono accettare di affidarsi solo ai limiti di ciascuna finestra. Una finestra con dati di origine obsoleti non diventa mai una regola di negazione; le regole ordinarie e le concessioni permanenti restano percorsi di riserva.
Algoritmo del produttore
Usa questo flusso indipendente dal linguaggio per ogni aggiornamento di un membro:
function publish_member(member_key):
source_result = fetch_authoritative_access_state(member_key)
if source_result.failed or source_result.incomplete:
record_failure(member_key)
schedule_retry(member_key)
return without sending access_time_windows
windows = []
for source_window in source_result.current_windows:
if source_window.is_absolute:
windows.append({
external_id: stable_window_id(source_window),
kind: "absolute",
from: to_integer_unix_seconds(source_window.start_instant),
to: to_integer_unix_seconds(source_window.end_instant),
timezone: source_window.display_timezone or "UTC",
scope_key: stable_scope_key(source_window) or "default",
label: safe_label(source_window)
})
else:
windows.append({
external_id: stable_window_id(source_window),
kind: "weekly",
timezone: valid_iana_zone(source_window.timezone) or "UTC",
access_hours: split_validate_and_sort_weekly_blocks(source_window),
active_from: optional_integer_unix_seconds(source_window.active_start),
active_until: optional_integer_unix_seconds(source_window.active_end),
scope_key: stable_scope_key(source_window) or "default",
label: safe_label(source_window)
})
normalized_snapshot = validate_sort_and_deduplicate(windows)
revision = next_durable_member_revision(member_key)
request = member_upsert_with(normalized_snapshot, revision)
persist_for_retry(member_key, request)
response = post_request(request)
if response.is_transient_or_unknown:
retry_exact_request_with_same_revision(request)
else if response.is_revision_conflict:
reconcile_with_latest_committed_source_state(member_key)
else if response.is_stale_skip:
stop_replaying_this_revision_and_reconcile(member_key)
Elenco di controllo per la certificazione
Completa queste verifiche in una struttura di staging usando la mappatura scope_key definitiva:
- Una finestra assoluta viene accettata con il fuso orario omesso e viene valutata in UTC.
- Una finestra assoluta con fuso orario IANA personalizzato mantiene gli stessi istanti
from/to. - Una finestra settimanale con fuso orario IANA personalizzato segue l'ora locale durante il passaggio all'ora legale e il ritorno all'ora solare.
- Un'istantanea mista assoluta/settimanale sostituisce completamente l'istantanea precedente.
- L'accesso inizia esattamente a
frome termina esattamente ato; anche le fini dei blocchi settimanali sono escluse. - L'aggiornamento degli orari con lo stesso
external_ide una revisione più recente ha effetto. - L'omissione di
access_time_windowsmodifica gli altri campi del Member senza modificare le finestre. - La ripetizione della stessa revisione/dello stesso payload è idempotente.
- La stessa revisione con un payload diverso restituisce un conflitto e non modifica l'istantanea.
- Una revisione più vecchia arrivata fuori ordine viene segnalata come obsoleta e non può riportare indietro lo stato.
- Un
[]più recente revoca le finestre di proprietà della fonte; un errore di recupero a monte non invia nulla. - Uno
scope_keysconosciuto o non mappato nega l'accesso e genera un avviso visibile agli operatori. - Gli avvisi sull'età dei dati di origine e i criteri configurati per i dati obsoleti funzionano alla soglia concordata.
- I percorsi del webhook e della riconciliazione completa convergono sulla stessa istantanea e sullo stesso ordine delle revisioni.
- La consegna del flusso raggiunge Door & Gate Access entro l'obiettivo concordato.
- Una finestra attiva può consentire l'accesso alla porta fisica mappata, ma non può aggirare la disattivazione dell'utente,
blockAllAccess, la manutenzione o il geofencing. - Prima, al di fuori e dopo una finestra, le regole ordinarie e le concessioni permanenti continuano a consentire l'accesso agli utenti che ne hanno comunque diritto.
Vettori di test minimi
[
{
"name": "absolute-half-open",
"window": {
"external_id": "tv-absolute",
"kind": "absolute",
"from": 1792053000,
"to": 1792058400
},
"expect": {
"1792052999": "inactive",
"1792053000": "active",
"1792058399": "active",
"1792058400": "inactive"
}
},
{
"name": "weekly-dst",
"window": {
"external_id": "tv-weekly-dst",
"kind": "weekly",
"timezone": "America/New_York",
"access_hours": {
"sun": [["01:00", "03:30"]],
"mon": [],
"tue": [],
"wed": [],
"thu": [],
"fri": [],
"sat": []
}
},
"expect": "Evaluate local wall time: skipped times do not occur; both repeated 01:30 instants qualify."
}
]
Invia anche vettori negativi per millisecondi Unix, stringhe numeriche, numeri in virgola mobile, fusi IANA non validi, from >= to, external_id duplicati, blocchi sovrapposti, blocchi notturni, più di otto blocchi in un giorno, più di 50 finestre e chiavi di ambito non mappate.