# 파트너 키(레거시) 이 방식은 기존 연동만 쓰는 옛 길입니다. 새 파트너 키는 발급하지 않습니다. 새 연동은 [앱 연결](/docs/auth-oauth)을 씁니다. ## 키와 토큰 교환 온보딩 때 두 세트를 받습니다. - 테스트 매장 `placeId`와 `spk_test_` 키 - 실매장 `placeId`와 `spk_live_` 키 `POST /core/v1/auth/apikey/token`으로 키를 15분 토큰으로 바꿉니다. 이후 모든 요청에 `Authorization: Bearer {accessToken}`을 붙입니다. 교환은 IP당 분당 30회입니다. 넘으면 429가 돌아옵니다. ```text → {"success":true,"data":{"accessToken":"…","expiresIn":900,"placeId":"…","scopes":["space:partner","device:door"]}} ``` `placeId`가 매장 ID입니다. 활동 기록 조회처럼 경로에 매장 ID가 필요한 API에 이 값을 그대로 넣습니다. 키가 매장에 묶여 있어 따로 물을 필요가 없습니다. ### 범위 키에는 쓸 수 있는 범위가 담깁니다. 발급할 때 요청한 범위대로 만들어지고, 범위 밖 호출은 403으로 거절됩니다. | 범위 | 이름 | 할 수 있는 것 | |---|---|---| | `space:partner` | 손님과 출입 | 출입 허가 등록·조회·해지, 출입 기록, 출입 설정, 회원권, 예약 | | `device:door` | 기기 제어 | 문 목록, 문 열기·잠그기·열어두기 | 기기 제어가 담긴 키는 물리적인 문을 엽니다. 유출됐다면 즉시 알려 주세요. 폐기하면 15분 안에 모든 호출이 거절됩니다. 문 명령은 [기기](/docs/devices)와 같은 경로를 씁니다. ## 테스트 매장 `spk_test_` 키는 테스트 매장 전용입니다. 모의 응답이 아니라 실매장과 같은 서버, 같은 로직으로 동작합니다. 허가, 상태 전이, 기록이 모두 실제로 남습니다. 실제 문과는 연결되지 않습니다. - 테스트 매장에는 문이 등록되어 있습니다. 문 열기·잠그기·열어두기·정책까지 성공 응답으로 연습합니다. 실행 결과는 출입 기록에 남습니다. - 키가 매장에 묶여 있어 테스트 호출이 실매장 문을 여는 일은 없습니다. - 전화 입장은 실매장에 050 출입 전화번호가 배정된 뒤 동작합니다. 배정 여부는 [출입 설정](#출입-설정)의 `entryPhone`으로 확인합니다. - 실운영 전환은 `spk_live_` 키로 바꾸면 끝입니다. 코드는 그대로입니다. ## 입장 수단 허가를 등록하면 두 방법이 모두 동작합니다. | 수단 | 동작 | |---|---| | 전화 | 등록된 번호로 매장 출입 전화번호(050)에 전화하면 발신번호를 확인하고 문이 열립니다. 번호는 [출입 설정](#출입-설정)의 `entryPhone`입니다. 안내물에 `tel:0507…` QR로 인쇄하면 찍는 즉시 전화가 걸립니다. | | 입장 링크(QR) | 허가 응답의 `url`을 앱, 문자, 인쇄물에 QR로 넣습니다. 손님이 링크를 열면 문 버튼이 나타납니다. 링크가 곧 자격이므로 공개 채널에 올리지 마세요. 해지하면 즉시 무효입니다. | ## 출입 허가 ### 등록 `POST /space/v1/partner/entry-permits` ```json {"externalRef":"member-1001","phone":"01012345678","name":"김회원", "startsAt":"2026-09-03T00:00:00Z","endsAt":"2026-09-05T00:00:00Z"} ``` ```json {"success":true,"data":{"permitId":"…","entryToken":"…", "url":"https://go.superplace.so/entry?t=…"}} ``` - 같은 `externalRef`로 다시 호출하면 기간이 바뀌고 해지가 풀립니다. 같은 요청을 몇 번 보내도 결과는 한 번 보낸 것과 같습니다(멱등). - 시간창은 한 건당 최대 720시간입니다. 장기 자격은 재등록하거나 회원권을 사용합니다. #### doorIds `doorIds`는 허용할 문을 지정합니다. - 값은 [기기 목록](/docs/devices)이 돌려주는 이 매장의 문 `id`여야 합니다. 문이 아니거나 다른 매장의 id가 섞이면 400입니다. - 새 `externalRef`에서 생략하거나 `null`이면 현재 매장의 출입문 범위가 적용됩니다. - 같은 `externalRef`를 재발급할 때 생략하거나 `null`이면 저장된 문 범위가 유지됩니다. - 비어 있지 않은 배열을 보낼 때만 범위가 바뀝니다. 빈 배열은 400입니다. - 모든 문으로 넓히려면 현재 문 ID 전체를 명시합니다. 등록 성공은 `201`입니다. 같은 `externalRef`로 다시 보내 기간만 바꾸는 경우도 `201`입니다. ### 해지 `POST /space/v1/partner/entry-permits/revoke` ```text {"externalRef":"member-1001","reason":"환불"} 또는 {"permitId":"…"} ``` `reason`은 선택이고 200자 이하입니다. ### 목록 `GET /space/v1/partner/entry-permits` 쿼리는 `externalRef`, `limit`(500 이하), `offset`입니다. 파트너가 등록한 허가만 반환합니다. ```text → data: [{"permitId":"…","externalRef":"member-1001","name":"김회원","phoneTail":"5678", "state":"active", // active | pending | expired | revoked "startsAt":"…","endsAt":"…","useCount":3,"firstUsedAt":"…","url":"…"}] ``` ### 단건 `GET /space/v1/partner/entry-permits/{permitId}` ## 회원권과 예약 허가, 회원권, 예약은 병렬로 판정합니다. 하나라도 유효하면 문이 열립니다. - 회원권이 살아 있으면 허가를 해지해도 열립니다. 시간 제한 운영은 허가만 사용하세요. - `mode:free` 시간대는 자격 판정 없이 개방됩니다. 허가와 별개의 축입니다. 파트너 키로 예약을 만들 때는 `POST /space/v1/partner/reservations`를 씁니다. `externalRef`가 파트너 쪽 예약 번호이고, 같은 번호로 다시 보내면 같은 예약을 돌려줍니다. ## 출입 기록 문 명령은 모두 출입 기록에 남습니다. 테스트 매장의 실행 결과도 같습니다. 조회는 [API 레퍼런스](/api)를 참고하세요. ## 출입 설정 출입 설정 조회에서 `entryPhone`(매장 출입 전화번호)을 확인합니다. 실매장에 050 번호가 배정되기 전에는 전화 입장이 동작하지 않습니다. ## 영업시간 `PATCH /core/v1/places/{placeId}` ```text {"hours":{"week":{"mon":[["09:00","22:00"]], …, "sun":[]}}} # 빈 배열 = 휴무 ``` `GET /core/v1/public/places/{placeId}/schedule?days=7` 휴무와 공휴일까지 계산된, 그날 실제로 여는 구간을 돌려줍니다. 인증이 필요 없습니다. API 키로는 영업시간만 수정할 수 있습니다. 다른 매장 정보는 403입니다. 저장하면 영업시간 기준으로 도는 매장 자동화가 즉시 이 값을 따릅니다. ## 명부 반입 기존 시스템의 명부를 옮길 때 일괄 반입을 사용합니다. | 대상 | 경로 | |---|---| | 손님 | `/space/v1/customers/import` | | 회원권 | `/space/v1/memberships/import` | 본문은 `{"source":"어느 시스템","items":[…]}` 형식입니다. 행마다 독립으로 처리합니다. 같은 파일을 다시 올려도 중복이 생기지 않습니다. ## 오프라인 동작 - 허가 등록과 해지는 최대 5분 안에 매장 장비로 동기화됩니다. - 매장 인터넷이 끊긴 동안에는 이미 동기화된 허가만 동작합니다. 끊긴 뒤의 등록과 해지는 복구 전까지 반영되지 않습니다. - 끊김이 36시간을 넘으면 현장 판정이 중단됩니다. 문은 보드에 저장된 끊김 동작대로 움직입니다. 매장이 콘솔에서 정합니다. - 전화 판정은 서버에서 하므로 매장 회선과 무관합니다. 문을 여는 명령을 전달하려면 매장 회선이 필요합니다.