Toegangstijdvensters

Met Access Time Windows kan een Partner Member tijdelijke of terugkerende toegang meekrijgen die door een extern systeem wordt aangeleverd. Deze pagina is de gezaghebbende handleiding voor producenten van access_time_windows en access_time_windows_revision.

Access Time Windows zijn aanvullende rechten die alleen toegang kunnen toestaan. Een actief venster kan toegang toestaan, maar een inactief, verlopen, verouderd of niet-toegewezen venster weigert geen toegang die een reguliere regel of een permanent toegangsrecht al toestaat. Het uitschakelen van gebruikers, expliciete deurblokkering, de onderhoudsmodus, geofencing, snelheidslimieten en de beschikbaarheid van controllers blijven van toepassing.

Implementaties voor specifieke upstream systemen zijn afzonderlijke adapters. Ze moeten hun brongegevens vertalen naar dit leverancierneutrale contract zonder de betekenis van de velden te veranderen.

Velden van de momentopname van een lid

  • access_time_windows array optioneel

    De volledige momentopname van Access Time Windows voor dit lid en deze partnerbron. Een momentopname kan absolute- en weekly-vensters combineren. Laat het veld weg om de opgeslagen momentopname ongewijzigd te laten; stuur [] om elk venster van deze bron in te trekken.

  • access_time_windows_revision integer verplicht met access_time_windows

    Een monotoon oplopend, niet-negatief JSON-veilig geheel getal voor de revisiereeks van dit lid/deze bron. Het is verplicht wanneer access_time_windows aanwezig is en mag niet groter zijn dan 9007199254740991.

Elke momentopname accepteert maximaal 50 vensters. external_id, scope_key en label accepteren maximaal 120 tekens. Absolute vensters kunnen maximaal 90 dagen beslaan. Een weekdag accepteert maximaal acht niet-overlappende blokken. Ongeldige gegevens worden geweigerd in plaats van ingekort of geraden.

Vormen van vensters

Een absoluut venster geeft één exact interval aan:

{
  "external_id": "access-4b1782",
  "kind": "absolute",
  "from": 1792053000,
  "to": 1792058400,
  "timezone": "Australia/Perth",
  "scope_key": "after_hours_entry",
  "label": "Temporary site access"
}

Verplichte velden zijn external_id, kind, from en to. timezone, scope_key en label zijn optioneel.

Een wekelijks venster geeft terugkerende intervallen in lokale kloktijd aan, eventueel begrensd door UTC-tijdstippen:

{
  "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"
}

Verplichte velden zijn external_id, kind en access_hours. timezone, active_from, active_until, scope_key en label zijn optioneel.

Volledig gecombineerd verzoek

Dit verzoek vervangt de vorige momentopname van de bron voor het lid door één absoluut en één wekelijks venster:

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"
          }
        ]
      }
    ]
  }'

Gebruik facility_id of linking_identifier, afhankelijk van je geconfigureerde koppelmethode. Stuur niet beide.

Regels voor tijden en grenzen

from, to, active_from en active_until zijn JSON-gehele getallen in Unix-seconden die UTC-tijdstippen aangeven:

  • Stuur een JSON-geheel getal zonder aanhalingstekens. Numerieke tekenreeksen, drijvendekommagetallen, RFC 3339-tekenreeksen en Unix-milliseconden worden geweigerd.
  • Elk interval is halfopen: [from, to). Toegang kan precies op from beginnen; precies op to is die niet meer actief.
  • from moet kleiner zijn dan to. Als beide wekelijkse grenzen aanwezig zijn, moet active_from kleiner zijn dan active_until.
  • Leid epochs niet af van een lokale datum/tijd zonder tijdzone. Zet de brontijd eerst om naar een UTC-tijdstip, deel daarna de Unix-milliseconden door 1000 en controleer of het resultaat een geheel getal is.

Epochconversie

2026-10-15T08:30:00Z wordt omgezet naar 1792053000:

UTC instant                 Unix milliseconds   Unix seconds sent
2026-10-15T08:30:00Z        1792053000000        1792053000

Taalonafhankelijke conversie:

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

Als de bron een precisie onder de seconde heeft, leg dan vóór het testen van de integratie bewust een afrondingsbeleid voor begin en einde vast. Geef nooit milliseconden door in de verwachting dat de API de eenheid zelf afleidt.

Tijdzones en zomertijd

timezone moet een geldige IANA-tijdzone zijn, zoals UTC, Australia/Perth of America/New_York. Als dit veld wordt weggelaten, is de standaardwaarde UTC.

  • Absolute vensters: from en to geven al exacte UTC-tijdstippen aan. timezone is alleen metadata voor weergave en audit; een wijziging ervan verschuift geen van beide tijdstippen.
  • Wekelijkse vensters: timezone bepaalt de lokale kloktijd en de zomertijdregels waarmee access_hours wordt geëvalueerd. Zet wekelijkse blokken niet vooraf om naar UTC.
  • Bij de overgang naar zomertijd komt een lokale tijd die de tijdzone overslaat nooit voor.
  • Bij de overgang naar wintertijd vallen beide echte tijdstippen met de herhaalde lokale tijd binnen het wekelijkse blok.
  • active_from en active_until blijven UTC-tijdstippen, ook wanneer een wekelijks venster een andere tijdzone dan UTC gebruikt.

access_hours gebruikt sun tot en met sat in kleine letters. Elke waarde is een array van [start, end]-paren in 24-uursnotatie HH:mm met voorloopnullen. Blokken mogen niet leeg zijn, moeten op volgorde staan en mogen niet overlappen. end is exclusief. Een blok kan niet over middernacht heen lopen; splits een nachtelijk broninterval in een laat blok op de eerste dag en een vroeg blok op de volgende dag.

Stabiele ID's en scopetoewijzing

external_id moet stabiel zijn gedurende de levensduur van een logisch venster en uniek binnen de momentopname van het lid. Houd het stabiel wanneer tijden of labels veranderen, zodat beheerders en auditrecords revisies aan elkaar kunnen koppelen. Gebruik het niet opnieuw voor een venster dat er niets mee te maken heeft.

scope_key is je stabiele logische toegangsdoel. De standaardwaarde is default. Een beheerder van de faciliteit wijst elke toegestane sleutel toe aan doelen in Performance Hub: de hele faciliteit, een deur of een Toegangsbereik:

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

Partners sturen binnen een venster geen ID's van faciliteiten, deuren of Toegangsbereiken in Performance Hub mee. Een onbekende, uitgeschakelde of niet-toegewezen scope_key leidt voor dat venster tot weigering (fail-closed); de toegang wordt er niet ruimer door. Spreek toewijzingen af en test ze vóór de lancering.

Volledige momentopnamen en revisies

Behandel de twee velden als een afzonderlijk geversioneerde partitie binnen de upsert van een lid:

InvoerResultaat
access_time_windows weggelatenBestaande momentopname blijft ongewijzigd; er is geen revisie nodig
Aanwezig met een nieuwere revisieDe volledige opgeslagen momentopname voor dit lid/deze bron wordt vervangen
Dezelfde revisie en dezelfde genormaliseerde payloadIdempotente herhaling; de momentopname verandert niet
Dezelfde revisie en een andere genormaliseerde payloadConflict; ken na reconciliatie een nieuwe revisie toe
Oudere revisieVerouderde momentopname wordt overgeslagen; deze kan een nieuwere status niet overschrijven
access_time_windows: [] met een nieuwere revisieTrekt expliciet alle vensters van de bron in

Een momentopname is volledig, geen patch. Als revisie 1842 de vensters A en B bevat, verwijdert revisie 1843 met alleen B het venster A.

Houd per partner/lid één gecoördineerde revisiereeks bij over webhookworkers, polling, herhaling en volledige reconciliatie heen. Een duurzame databasesequentie heeft de voorkeur. Een waarde in Unix-milliseconden kan alleen als revisie worden gebruikt wanneer alle schrijvers zich coördineren en een strikte volgorde en uniciteit garanderen; het is een revisiewaarde, geen epochveld.

Bewaar het exacte verzoek en de revisie totdat de API-response bekend is. Probeer na een time-out of tijdelijke fout dezelfde genormaliseerde momentopname opnieuw met dezelfde revisie. Ken geen nieuwe revisie toe alleen omdat de aflevering onzeker is.

Annulering, verloop en upstream fouten

  • Eén venster annuleren: haal met succes de gezaghebbende status bij de bron op, verwijder dat venster en stuur de resterende volledige momentopname met een nieuwere revisie.
  • Alle vensters annuleren: stuur access_time_windows: [] met een nieuwere revisie.
  • Een absoluut venster laten verlopen: het staat vanaf to geen toegang meer toe; stuur later een reconciliatie zonder het verlopen item om momentopnamen schoon te houden.
  • Een wekelijks venster beëindigen: verwijder het in een nieuwere momentopname, of gebruik active_until wanneer de bron een bekend eindtijdstip heeft.
  • Ophalen bij de bron mislukt of is onvolledig: stuur geen [] en publiceer geen gedeeltelijke momentopname. Geef een waarschuwing en probeer het ophalen opnieuw. Een upstream fout omzetten in een lege momentopname betekent een onbedoelde intrekking van toegang.
  • Verzoek aan de Partner API mislukt bij validatie: corrigeer de gegevens en stuur alleen een nieuwere revisie als de genormaliseerde momentopname verandert. Probeer tijdelijke fouten opnieuw met de oorspronkelijke revisie.

De actualiteit van de bron wordt per integratie geconfigureerd op basis van de toegezegde frequentie van aflevering en reconciliatie. Spreek vóór activering af wat de maximale leeftijd van de brongegevens is, wat er gebeurt als die wordt overschreden, wat de drempels voor waarschuwingen zijn en of beheerders mogen bevestigen dat alleen op de eigen grenzen van elk venster wordt vertrouwd. Een verouderd bronvenster wordt nooit een weigeringsregel; reguliere regels en permanente toegangsrechten blijven terugvalroutes.

Algoritme voor producenten

Gebruik dit taalonafhankelijke proces voor elke update van een lid:

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)

Certificeringschecklist

Voer deze controles uit in een testfaciliteit met de definitieve scope_key-toewijzing:

  • Een absoluut venster zonder tijdzone wordt geaccepteerd en als UTC geëvalueerd.
  • Een absoluut venster met een aangepaste IANA-tijdzone behoudt dezelfde from/to-tijdstippen.
  • Een wekelijks venster met een aangepaste IANA-tijdzone volgt de lokale kloktijd bij de overgangen naar zomer- en wintertijd.
  • Een gecombineerde absolute/wekelijkse momentopname vervangt de vorige momentopname volledig.
  • Toegang begint precies op from en stopt precies op to; de einden van wekelijkse blokken zijn ook exclusief.
  • Het bijwerken van tijden onder dezelfde external_id met een nieuwere revisie wordt van kracht.
  • Het weglaten van access_time_windows wijzigt andere velden van het lid zonder de vensters te wijzigen.
  • Het herhalen van dezelfde revisie/payload is idempotent.
  • Dezelfde revisie met een andere payload geeft een conflict en wijzigt de momentopname niet.
  • Een oudere revisie die in de verkeerde volgorde binnenkomt, wordt als verouderd gemeld en kan de status niet terugdraaien.
  • Een nieuwere [] trekt de vensters van de bron in; bij een mislukte upstream ophaalactie wordt niets verzonden.
  • Een onbekende of niet-toegewezen scope_key leidt tot weigering (fail-closed) en geeft een waarschuwing die beheerders kunnen zien.
  • Waarschuwingen over de leeftijd van brongegevens en het geconfigureerde beleid voor verouderde bronnen werken bij de afgesproken drempel.
  • De webhookroute en de route voor volledige reconciliatie komen uit op dezelfde momentopname en revisievolgorde.
  • Aflevering van de reeks bereikt Door & Gate Access binnen het afgesproken doel.
  • Een actief venster kan toegang geven tot de toegewezen fysieke deur, maar kan het uitschakelen van gebruikers, blockAllAccess, onderhoud of geofencing niet omzeilen.
  • Vóór, buiten en na een venster geven reguliere regels en permanente toegangsrechten nog steeds toegang aan gebruikers die daar anders recht op hebben.

Minimale testvectoren

[
  {
    "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."
  }
]

Lever ook negatieve vectoren aan voor Unix-milliseconden, numerieke tekenreeksen, drijvendekommagetallen, ongeldige IANA-zones, from >= to, dubbele external_id, overlappende blokken, nachtelijke blokken, meer dan acht blokken op een dag, meer dan 50 vensters en niet-toegewezen scopesleutels.