출입 시간대

Access Time Windows를 사용하면 Partner Member가 외부 시스템에서 제공하는 임시 또는 반복 출입 권한을 가질 수 있습니다. 이 페이지는 access_time_windows와 access_time_windows_revision에 대한 권위 있는 생산자 가이드입니다.

Access Time Windows는 허용만 하는 추가 권한입니다. 활성 시간 창은 출입을 허용할 수 있지만, 비활성, 만료, 오래되었거나 매핑되지 않은 시간 창이 일반 규칙이나 영구 권한에서 이미 허용한 출입을 거부하지는 않습니다. 사용자 비활성화, 명시적 출입문 차단, 유지 관리 모드, 지오펜싱, 속도 제한, 컨트롤러 가용성은 계속 적용됩니다.

특정 업스트림 시스템을 위한 구현은 별도의 어댑터입니다. 어댑터는 필드 의미를 변경하지 않고 원본 데이터를 이 공급업체 중립적인 계약으로 변환해야 합니다.

회원 스냅샷 필드

  • access_time_windows array 선택

    이 회원과 파트너 원본에 대한 완전한 Access Time Window 스냅샷입니다. 스냅샷에는 absolute 시간 창과 weekly 시간 창을 함께 포함할 수 있습니다. 저장된 스냅샷을 변경하지 않으려면 필드를 생략하고, 이 원본이 소유한 모든 시간 창을 취소하려면 []를 보내십시오.

  • access_time_windows_revision integer access_time_windows와 함께 필수

    이 회원/원본 리비전 스트림에 대해 단조 증가하는, 음수가 아니고 JSON에서 안전한 정수입니다. access_time_windows가 있을 때마다 필수이며 9007199254740991을 초과할 수 없습니다.

각 스냅샷에는 최대 50개의 시간 창을 넣을 수 있습니다. external_id, scope_key, label은 최대 120자까지 허용됩니다. 절대 시간 창은 최대 90일까지 지정할 수 있습니다. 주간 시간 창의 하루에는 겹치지 않는 블록을 최대 8개까지 넣을 수 있습니다. 잘못된 데이터는 잘라내거나 추측하지 않고 거부됩니다.

시간 창 형식

절대 시간 창은 하나의 정확한 구간을 나타냅니다.

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

필수 필드는 external_id, kind, from, to입니다. timezone, scope_key, label은 선택 사항입니다.

주간 시간 창은 반복되는 현지 벽시계 기준 구간을 나타내며, 선택적으로 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"
}

필수 필드는 external_id, kind, access_hours입니다. timezone, active_from, active_until, scope_key, label은 선택 사항입니다.

혼합된 전체 요청

이 요청은 회원의 이전 원본 소유 스냅샷을 하나의 절대 시간 창과 하나의 주간 시간 창으로 대체합니다.

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

구성된 연결 방법에 따라 facility_id 또는 linking_identifier를 사용하십시오. 둘 다 보내지 마십시오.

시간 및 경계 규칙

from, to, active_from, active_until은 UTC 시점을 나타내는 JSON 정수 Unix 초입니다.

  • 따옴표 없는 JSON 정수로 보내십시오. 숫자 문자열, 부동 소수점, RFC 3339 문자열, Unix 밀리초는 거부됩니다.
  • 모든 구간은 반개구간 [from, to)입니다. 출입은 정확히 from에 시작할 수 있으며, 정확히 to에서 더 이상 활성 상태가 아닙니다.
  • from은 to보다 작아야 합니다. 주간 경계가 둘 다 있는 경우 active_from은 active_until보다 작아야 합니다.
  • 시간대 정보가 없는 현지 날짜/시간에서 epoch를 도출하지 마십시오. 먼저 원본 시간을 UTC 시점으로 변환한 다음 Unix 밀리초를 1000으로 나누고 결과가 정수인지 확인하십시오.

epoch 변환

2026-10-15T08:30:00Z는 1792053000으로 변환됩니다.

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

언어 중립적인 변환:

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

원본에 1초 미만의 정밀도가 있다면 통합 테스트 전에 시작/종료 반올림 정책을 명확히 정의하십시오. 밀리초를 전달하고 API가 단위를 추론하리라 기대하지 마십시오.

시간대 및 일광 절약 시간

timezone은 UTC, Australia/Perth, America/New_York과 같은 유효한 IANA 시간대여야 합니다. 생략하면 기본값은 UTC입니다.

  • 절대 시간 창: from과 to는 이미 정확한 UTC 시점을 나타냅니다. timezone은 표시 및 감사용 메타데이터일 뿐이며, 변경해도 두 시점 모두 이동하지 않습니다.
  • 주간 시간 창: timezone은 access_hours를 평가하는 데 사용되는 현지 벽시계와 일광 절약 시간 규칙을 정의합니다. 주간 블록을 미리 UTC로 변환하지 마십시오.
  • 서머타임이 시작되어 시계가 앞당겨질 때 시간대에서 건너뛰는 현지 시간은 발생하지 않습니다.
  • 서머타임이 끝나 시계가 되돌려질 때 반복되는 현지 시간에 해당하는 실제 두 시점 모두 주간 블록과 일치합니다.
  • 주간 시간 창이 UTC가 아닌 시간대를 사용하더라도 active_from과 active_until은 UTC 시점으로 유지됩니다.

access_hours는 소문자 sun부터 sat까지 사용합니다. 각 값은 0으로 채운 24시간 형식 HH:mm을 사용하는 [start, end] 쌍의 배열입니다. 블록은 비어 있지 않고, 순서대로 정렬되어 있으며, 겹치지 않아야 합니다. end는 포함되지 않습니다. 블록은 자정을 넘을 수 없습니다. 야간 원본 구간은 첫째 날의 늦은 블록과 다음 날의 이른 블록으로 분할하십시오.

안정적인 ID 및 범위 매핑

external_id는 논리적 시간 창의 수명 동안 안정적이어야 하며 회원 스냅샷 내에서 고유해야 합니다. 운영자와 감사 기록이 리비전을 연관시킬 수 있도록 시간이나 레이블이 변경되어도 동일하게 유지하십시오. 관련 없는 시간 창에 다시 사용하지 마십시오.

scope_key는 안정적인 논리적 출입 대상입니다. 기본값은 default입니다. 시설 관리자는 허용된 각 키를 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

파트너는 시간 창 안에 Performance Hub 시설, 출입문 또는 접근 범위 ID를 보내지 않습니다. 알 수 없거나, 비활성화되었거나, 매핑되지 않은 scope_key는 해당 시간 창에 대해 거부 방향으로 실패하며 출입 범위를 넓히지 않습니다. 출시 전에 매핑을 합의하고 테스트하십시오.

완전한 스냅샷 및 리비전

두 필드를 회원 업서트 내에서 별도로 버전이 관리되는 파티션으로 취급하십시오.

입력결과
access_time_windows 생략기존 스냅샷이 변경되지 않으며 리비전이 필요하지 않습니다
더 새로운 리비전과 함께 제공이 회원/원본에 대해 저장된 스냅샷 전체가 대체됩니다
같은 리비전과 같은 정규화 페이로드멱등 재전송이며 스냅샷이 변경되지 않습니다
같은 리비전과 다른 정규화 페이로드충돌입니다. 조정한 후 새 리비전을 할당하십시오
이전 리비전오래된 스냅샷은 건너뛰며 더 새로운 상태를 덮어쓸 수 없습니다
더 새로운 리비전과 함께 access_time_windows: []원본 소유 시간 창을 모두 명시적으로 취소합니다

스냅샷은 패치가 아닌 완전한 데이터입니다. 리비전 1842에 시간 창 A와 B가 있는 경우, B만 포함된 리비전 1843은 A를 제거합니다.

웹훅 작업자, 폴링, 재전송, 전체 조정 전반에서 파트너/회원별로 조율된 하나의 리비전 스트림을 유지하십시오. 지속적인 데이터베이스 시퀀스를 권장합니다. Unix 밀리초 값은 모든 작성자가 조율하여 엄격한 순서와 고유성을 보장하는 경우에만 리비전으로 사용할 수 있습니다. 이는 리비전 값이지 epoch 필드가 아닙니다.

API 응답을 알 수 있을 때까지 정확한 요청과 리비전을 보존하십시오. 제한 시간 초과나 일시적인 오류 후에는 같은 정규화 스냅샷을 같은 리비전으로 재시도하십시오. 전송 결과가 불확실하다는 이유만으로 새 리비전을 할당하지 마십시오.

취소, 만료 및 업스트림 실패

  • 하나의 시간 창 취소: 신뢰할 수 있는 원본 상태를 성공적으로 조회하고, 해당 시간 창을 제거한 다음, 나머지 완전한 스냅샷을 더 새로운 리비전으로 보내십시오.
  • 모든 시간 창 취소: 더 새로운 리비전과 함께 access_time_windows: []를 보내십시오.
  • 절대 시간 창 만료 처리: to에서 허용이 중지됩니다. 스냅샷을 깔끔하게 유지하려면 이후 조정에서 만료된 항목을 제외하고 보내십시오.
  • 주간 시간 창 종료: 더 새로운 스냅샷에서 제거하거나, 원본에 알려진 종료 시점이 있으면 active_until을 사용하십시오.
  • 원본 조회 실패 또는 불완전: []를 보내지 말고 부분 스냅샷도 게시하지 마십시오. 알림을 보내고 조회를 재시도하십시오. 업스트림 실패를 빈 스냅샷으로 바꾸면 의도하지 않은 출입 권한 취소가 발생합니다.
  • Partner API 요청이 유효성 검사에 실패: 데이터를 수정하고, 정규화 스냅샷이 변경되는 경우에만 더 새로운 리비전을 보내십시오. 일시적인 실패는 원래 리비전으로 재시도하십시오.

원본 최신성은 통합별로 약속된 전송 및 조정 주기에 따라 구성됩니다. 활성화하기 전에 최대 원본 경과 시간, 이를 초과할 때의 처리, 알림 임계값, 운영자가 각 시간 창의 자체 경계에만 의존하는 것을 승인할 수 있는지 여부를 합의하십시오. 오래된 원본 시간 창은 절대 거부 규칙이 되지 않으며, 일반 규칙과 영구 권한이 대체 경로로 유지됩니다.

생산자 알고리즘

모든 회원 업데이트에 다음 언어 중립적인 워크플로를 사용하십시오.

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)

인증 체크리스트

최종 scope_key 매핑을 사용하여 스테이지 시설에서 다음 점검을 완료하십시오.

  • timezone을 생략한 절대 시간 창이 수락되고 UTC로 평가됩니다.
  • 사용자 지정 IANA 시간대를 사용하는 절대 시간 창이 동일한 from/to 시점을 유지합니다.
  • 사용자 지정 IANA 시간대를 사용하는 주간 시간 창이 서머타임 시작 및 종료 전환 시 현지 벽시계 시간을 따릅니다.
  • 절대/주간 혼합 스냅샷이 이전 스냅샷을 완전히 대체합니다.
  • 출입이 정확히 from에 시작하고 정확히 to에 중지됩니다. 주간 블록의 종료도 포함되지 않습니다.
  • 같은 external_id로 더 새로운 리비전에서 시간을 업데이트하면 적용됩니다.
  • access_time_windows를 생략하면 시간 창은 변경되지 않고 다른 회원 필드만 변경됩니다.
  • 같은 리비전/페이로드를 재전송하면 멱등하게 처리됩니다.
  • 같은 리비전/다른 페이로드는 충돌을 반환하고 스냅샷을 변경하지 않습니다.
  • 순서가 뒤바뀐 이전 리비전은 오래된 것으로 보고되며 상태를 되돌릴 수 없습니다.
  • 더 새로운 []는 원본 소유 시간 창을 취소하며, 업스트림 조회 실패 시에는 아무것도 보내지 않습니다.
  • 알 수 없거나 매핑되지 않은 scope_key는 거부 방향으로 실패하며 운영자에게 표시되는 경고를 생성합니다.
  • 원본 경과 시간 알림과 구성된 오래된 원본 정책이 합의된 임계값에서 작동합니다.
  • 웹훅 경로와 전체 조정 경로가 동일한 스냅샷과 리비전 순서로 수렴합니다.
  • 스트림 전송이 합의된 목표 시간 내에 Door & Gate Access에 도달합니다.
  • 활성 시간 창은 매핑된 물리적 출입문을 허용할 수 있지만, 사용자 비활성화, blockAllAccess, 유지 관리 또는 지오펜싱을 우회할 수 없습니다.
  • 시간 창 이전, 시간 창 외, 시간 창 이후에도 일반 규칙과 영구 권한은 다른 조건을 충족하는 사용자를 계속 허용합니다.

최소 테스트 벡터

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

Unix 밀리초, 숫자 문자열, 부동 소수점, 잘못된 IANA 시간대, from >= to, 중복된 external_id, 겹치는 블록, 야간 블록, 하루 8개 초과 블록, 50개 초과 시간 창, 매핑되지 않은 범위 키에 대한 부정 벡터도 제출하십시오.