Jendela Waktu Akses

Access Time Windows memungkinkan Partner Member membawa akses sementara atau berulang yang disediakan oleh sistem eksternal. Halaman ini adalah panduan produsen otoritatif untuk access_time_windows dan access_time_windows_revision.

Access Time Windows adalah izin tambahan yang hanya bersifat mengizinkan. Jendela yang aktif dapat mengizinkan akses, tetapi jendela yang tidak aktif, kedaluwarsa, usang, atau tidak dipetakan tidak menolak akses yang sudah diizinkan oleh aturan reguler atau izin permanen. Penonaktifan pengguna, pemblokiran pintu secara eksplisit, mode pemeliharaan, geofencing, batas laju, dan ketersediaan pengontrol tetap berlaku.

Implementasi untuk sistem upstream tertentu merupakan adaptor terpisah. Adaptor tersebut harus menerjemahkan data sumbernya ke dalam kontrak netral vendor ini tanpa mengubah makna bidangnya.

Bidang snapshot anggota

  • access_time_windows array opsional

    Snapshot Access Time Window lengkap untuk anggota dan sumber mitra ini. Satu snapshot dapat menggabungkan jendela absolute dan weekly. Hilangkan bidang ini agar snapshot yang tersimpan tidak berubah; kirim [] untuk mencabut setiap jendela yang dimiliki sumber ini.

  • access_time_windows_revision integer wajib bersama access_time_windows

    Bilangan bulat non-negatif yang aman untuk JSON dan terus meningkat secara monoton untuk aliran revisi anggota/sumber ini. Bidang ini wajib setiap kali access_time_windows ada dan tidak boleh melebihi 9007199254740991.

Setiap snapshot menerima paling banyak 50 jendela. external_id, scope_key, dan label menerima paling banyak 120 karakter. Jendela absolut dapat mencakup paling lama 90 hari. Satu hari mingguan menerima paling banyak delapan blok yang tidak tumpang tindih. Data yang tidak valid ditolak, bukan dipotong atau ditebak.

Bentuk jendela

Jendela absolut mengidentifikasi satu interval yang tepat:

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

Bidang wajib adalah external_id, kind, from, dan to. timezone, scope_key, dan label bersifat opsional.

Jendela mingguan mengidentifikasi interval waktu jam dinding lokal yang berulang, yang secara opsional dibatasi oleh instan 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"
}

Bidang wajib adalah external_id, kind, dan access_hours. timezone, active_from, active_until, scope_key, dan label bersifat opsional.

Permintaan gabungan lengkap

Permintaan ini mengganti snapshot milik sumber sebelumnya untuk anggota tersebut dengan satu jendela absolut dan satu jendela mingguan:

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

Gunakan facility_id atau linking_identifier sesuai dengan metode penautan yang Anda konfigurasi. Jangan kirim keduanya.

Aturan waktu dan batas

from, to, active_from, dan active_until adalah bilangan bulat JSON berupa detik Unix yang mengidentifikasi instan UTC:

  • Kirim bilangan bulat JSON tanpa tanda kutip. String numerik, bilangan float, string RFC 3339, dan milidetik Unix ditolak.
  • Setiap interval bersifat setengah terbuka: [from, to). Akses dapat dimulai tepat pada from; akses tidak lagi aktif tepat pada to.
  • from harus lebih kecil dari to. Jika kedua batas mingguan ada, active_from harus lebih kecil dari active_until.
  • Jangan menurunkan epoch dari tanggal/waktu lokal tanpa zona waktu. Selesaikan terlebih dahulu waktu sumber menjadi instan UTC, lalu bagi milidetik Unix dengan 1000 dan pastikan hasilnya berupa bilangan bulat.

Konversi epoch

2026-10-15T08:30:00Z dikonversi menjadi 1792053000:

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

Konversi yang netral bahasa:

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

Jika sumber memiliki presisi di bawah satu detik, tetapkan kebijakan pembulatan awal/akhir yang disengaja sebelum pengujian integrasi. Jangan pernah mengirimkan milidetik dan mengandalkan API untuk menebak satuannya.

Zona waktu dan waktu musim panas

timezone harus berupa zona waktu IANA yang valid seperti UTC, Australia/Perth, atau America/New_York. Nilai default-nya adalah UTC jika dihilangkan.

  • Jendela absolut: from dan to sudah mengidentifikasi instan UTC yang tepat. timezone hanya merupakan metadata tampilan dan audit; mengubahnya tidak pernah menggeser kedua instan tersebut.
  • Jendela mingguan: timezone menentukan jam dinding lokal dan aturan waktu musim panas yang digunakan untuk mengevaluasi access_hours. Jangan mengonversi blok mingguan ke UTC terlebih dahulu.
  • Saat perubahan maju (spring-forward), waktu lokal yang dilewati oleh zona waktu tidak pernah terjadi.
  • Saat perubahan mundur (fall-back), kedua instan nyata yang memiliki waktu lokal berulang tersebut cocok dengan blok mingguan.
  • active_from dan active_until tetap merupakan instan UTC meskipun jendela mingguan menggunakan zona waktu selain UTC.

access_hours menggunakan huruf kecil sun hingga sat. Setiap nilai berupa array pasangan [start, end] dengan format 24 jam HH:mm yang diawali nol. Blok tidak boleh kosong, harus berurutan, dan tidak boleh tumpang tindih. end bersifat eksklusif. Blok tidak boleh melewati tengah malam; pisahkan interval sumber semalam menjadi blok larut pada hari pertama dan blok pagi pada hari berikutnya.

ID stabil dan pemetaan cakupan

external_id harus stabil selama masa berlaku jendela logis dan unik di dalam snapshot anggota. Pertahankan agar tetap stabil saat waktu atau label berubah sehingga operator dan catatan audit dapat menghubungkan revisi. Jangan menggunakannya kembali untuk jendela yang tidak terkait.

scope_key adalah target akses logis Anda yang stabil. Nilai default-nya adalah default. Administrator fasilitas memetakan setiap kunci yang diizinkan ke target di seluruh fasilitas, pintu, atau Ruang Lingkup Akses di Performance Hub:

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

Mitra tidak mengirimkan ID fasilitas, pintu, atau Ruang Lingkup Akses Performance Hub di dalam jendela. Untuk scope_key yang tidak dikenal, dinonaktifkan, atau tidak dipetakan, jendela tersebut tidak memberikan akses apa pun; kunci tersebut tidak memperluas akses. Sepakati dan uji pemetaan sebelum peluncuran.

Snapshot lengkap dan revisi

Perlakukan kedua bidang ini sebagai partisi berversi terpisah di dalam upsert Member:

InputHasil
access_time_windows dihilangkanSnapshot yang ada tetap tidak berubah; revisi tidak diperlukan
Ada dengan revisi yang lebih baruSeluruh snapshot tersimpan untuk anggota/sumber ini diganti
Revisi sama dan payload ternormalisasi samaPemutaran ulang idempoten; snapshot tidak berubah
Revisi sama dan payload ternormalisasi berbedaKonflik; alokasikan revisi baru setelah rekonsiliasi
Revisi lebih lamaSnapshot usang dilewati; tidak dapat menimpa status yang lebih baru
access_time_windows: [] dengan revisi yang lebih baruSecara eksplisit mencabut semua jendela milik sumber

Snapshot bersifat lengkap, bukan patch. Jika revisi 1842 memiliki jendela A dan B, maka revisi 1843 yang hanya berisi B akan menghapus A.

Kelola satu aliran revisi terkoordinasi per mitra/anggota di seluruh worker webhook, polling, pemutaran ulang, dan rekonsiliasi penuh. Sekuens database yang tahan lama lebih disarankan. Nilai milidetik Unix dapat digunakan sebagai revisi hanya jika setiap penulis berkoordinasi dan menjamin urutan serta keunikan yang ketat; nilai ini adalah nilai revisi, bukan bidang epoch.

Simpan permintaan dan revisi yang persis sama hingga respons API diketahui. Coba ulang snapshot ternormalisasi yang sama dengan revisi yang sama setelah batas waktu atau kesalahan sementara. Jangan mengalokasikan revisi baru hanya karena pengiriman transport tidak pasti.

Pembatalan, kedaluwarsa, dan kegagalan upstream

  • Membatalkan satu jendela: berhasil mengambil status sumber otoritatif, hapus jendela tersebut, dan kirim snapshot lengkap yang tersisa dengan revisi yang lebih baru.
  • Membatalkan semua jendela: kirim access_time_windows: [] dengan revisi yang lebih baru.
  • Membiarkan jendela absolut kedaluwarsa: jendela berhenti mengizinkan pada to; kirim rekonsiliasi berikutnya tanpa entri yang kedaluwarsa agar snapshot tetap bersih.
  • Mengakhiri jendela mingguan: hapus jendela tersebut dalam snapshot yang lebih baru, atau gunakan active_until jika sumber memiliki instan akhir yang diketahui.
  • Pengambilan sumber gagal atau tidak lengkap: jangan kirim [] dan jangan memublikasikan snapshot parsial. Kirim peringatan dan coba ulang pengambilan. Mengubah kegagalan upstream menjadi snapshot kosong merupakan pencabutan akses yang tidak disengaja.
  • Permintaan Partner API gagal validasi: perbaiki data dan kirim revisi yang lebih baru hanya jika snapshot ternormalisasi berubah. Coba ulang kegagalan sementara dengan revisi aslinya.

Kesegaran sumber dikonfigurasi per integrasi berdasarkan frekuensi pengiriman dan rekonsiliasi yang dijanjikan. Sebelum aktivasi, sepakati usia sumber maksimum, apa yang terjadi jika batas tersebut terlampaui, ambang peringatan, dan apakah operator boleh menyetujui untuk hanya mengandalkan batas masing-masing jendela. Jendela sumber yang usang tidak pernah menjadi aturan penolakan; aturan reguler dan izin permanen tetap menjadi jalur cadangan.

Algoritma produsen

Gunakan alur kerja yang netral bahasa ini untuk setiap pembaruan anggota:

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)

Daftar periksa sertifikasi

Selesaikan pemeriksaan ini di fasilitas staging menggunakan pemetaan scope_key final:

  • Jendela absolut diterima dengan zona waktu dihilangkan dan dievaluasi sebagai UTC.
  • Jendela absolut dengan IANA kustom mempertahankan instan from/to yang sama.
  • Jendela mingguan dengan IANA kustom mengikuti waktu jam dinding lokal melewati transisi maju (spring-forward) dan mundur (fall-back).
  • Snapshot gabungan absolut/mingguan mengganti snapshot sebelumnya sepenuhnya.
  • Akses dimulai tepat pada from dan berhenti tepat pada to; akhir blok mingguan juga bersifat eksklusif.
  • Memperbarui waktu di bawah external_id yang sama dengan revisi yang lebih baru akan berlaku.
  • Menghilangkan access_time_windows mengubah bidang Member lainnya tanpa mengubah jendela.
  • Memutar ulang revisi/payload yang sama bersifat idempoten.
  • Revisi sama/payload berbeda mengembalikan konflik dan tidak mengubah snapshot.
  • Revisi lama yang datang tidak berurutan dilaporkan usang dan tidak dapat mengembalikan status ke belakang.
  • [] yang lebih baru mencabut jendela milik sumber; kegagalan pengambilan upstream tidak mengirimkan apa pun.
  • scope_key yang tidak dikenal atau tidak dipetakan tidak memberikan akses apa pun dan memunculkan peringatan yang terlihat oleh operator.
  • Peringatan usia sumber dan kebijakan sumber usang yang dikonfigurasi berfungsi pada ambang yang disepakati.
  • Jalur webhook dan rekonsiliasi penuh menghasilkan snapshot dan urutan revisi yang sama.
  • Pengiriman aliran mencapai Door & Gate Access dalam target yang disepakati.
  • Jendela aktif dapat mengizinkan pintu fisik yang dipetakan, tetapi tidak dapat melewati penonaktifan pengguna, blockAllAccess, pemeliharaan, atau geofencing.
  • Sebelum, di luar, dan setelah jendela, aturan reguler dan izin permanen tetap mengizinkan pengguna yang memenuhi syarat.

Vektor uji minimum

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

Kirimkan juga vektor negatif untuk milidetik Unix, string numerik, bilangan float, zona IANA yang tidak valid, from >= to, external_id duplikat, blok yang tumpang tindih, blok semalam, lebih dari delapan blok dalam sehari, lebih dari 50 jendela, dan kunci cakupan yang tidak dipetakan.