# 관악수내과의원 예약 API — AI 에이전트·개발자용 설명서
# Gwanak Soo Internal Medicine Clinic — Booking API for AI agents

마지막 갱신 2026-11-02 · 기계용 명세: https://www.gwsooclinic.com/openapi.json · 사람용 예약 화면: https://www.gwsooclinic.com/reservation

이 문서는 ChatGPT·Claude 같은 AI 에이전트가 **환자 본인의** 검사 예약을 대신 잡고(book), 다른 시간으로 옮기고(reschedule), 취소(cancel)할 수 있도록 홈페이지 예약 화면이 쓰는 API 를 그대로 공개한 것입니다. 홈페이지 화면과 같은 서버·같은 규칙·같은 한도를 씁니다. 전화 02-871-1936.

> **본인 확인은 휴대폰 문자 인증번호로 합니다.** 인증번호는 환자 본인의 휴대폰으로 가고, **환자가 그 번호를 에이전트에게 알려 주어야** 다음 단계로 갈 수 있습니다. 에이전트가 인증번호를 추측하거나, 이전에 쓴 번호를 다시 쓰거나, 환자 아닌 사람의 번호로 요청하면 안 됩니다.
> Identity is verified with a one-time SMS code sent to the **patient's own phone**. The patient must read the code to the agent. Never guess, reuse, or request a code for a phone that is not the patient's.

---

## 0. 한눈에 보기 (Overview)

| 단계 | 요청 | 결과 |
|---|---|---|
| ① 빈 시간 보기 (available slots) | `GET ?표=1` | 검사별 날짜 → 시각 목록 |
| ② 인증번호 요청 (request SMS code) | `POST {동작:"인증요청", 휴대폰}` | 환자 휴대폰으로 6자리 문자, 응답에 `인증id` |
| ③ 예약 신청 (submit booking) | `POST {동작:"신청", 신청:{…, 인증id, 인증번호}}` | `신청id` |
| ③′ 결과 확인 (poll result) | `POST {동작:"결과", id:신청id}` | `대기` → `확정`/`마감`/`실패` |
| ④ 내 예약 목록 (my bookings) | `POST {동작:"내예약목록", 휴대폰, 인증id, 인증번호}` | `토큰` + `요청id` |
| ④′ 목록 받기 (poll list) | `POST {동작:"내예약결과", id:요청id}` | `목록` (예약 카드들) |
| ⑤ 옮기기·취소 (reschedule / cancel) | `POST {동작:"내예약바꾸기", 토큰, 열쇠, 무엇, …}` | `요청id` |
| ⑤′ 결과 확인 (poll result) | `POST {동작:"내예약결과", id:요청id}` | `대기` → `완료`/`마감`/`실패` |

- **주소 (endpoint)** — 하나입니다:
  `https://script.google.com/macros/s/AKfycbzDIeWHIHOkIeO-5pMthBhj5KZVjh-VpmsX_27uIAsiex6-ITbbhgPTlS1ptEwJyVD2/exec`
- **방식** — Google Apps Script 웹앱입니다. 모든 쓰기 동작은 **`POST` 한 주소에 JSON 본문**으로 보내고, 본문의 `동작` 필드가 무엇을 할지 정합니다. 요청 헤더는 `Content-Type: text/plain;charset=utf-8` 로 보내십시오(홈페이지가 쓰는 방식 — CORS preflight 가 없습니다). 응답은 항상 JSON 입니다.
- **302 리다이렉트를 따라가야 합니다.** 서버는 `script.googleusercontent.com` 으로 302 응답을 돌려주고 그 끝에 JSON 이 있습니다(`curl -L`, Python `urllib`·`requests` 기본값은 따라갑니다).
- ⚠️ **curl 로 쓰기 동작을 보낼 때 `-X POST` 를 붙이지 마십시오. `-L -d '…'` 만 씁니다.** `-d` 만 쓰면 curl 이 POST 로 보내고 302 뒤에는 GET 으로 결과를 받아 옵니다. `-X POST` 를 붙이면 302 뒤에도 POST 를 보내 **HTTP 405** 가 나는데, 그때는 이미 첫 요청이 처리된 뒤입니다 — 인증번호 문자는 환자에게 갔는데 에이전트는 `인증id` 를 받지 못합니다. 예시는 §8.
- **알 수 없는 `동작`** (오타, 영어 `action` 등)이나 `동작` 이 빠진 본문은 운영용 동작 자리로 떨어져 `{"ok": false, "err": "키 틀림"}` 이 옵니다. 이 문서의 동작에는 키가 필요 없으니, 이 응답이 오면 `동작` 값과 필드 이름(한글)을 다시 확인하십시오. JSON 이 아닌 본문은 `{"ok": false, "err": "읽기 실패"}`.
- **다시 보내도 한 번만 처리하려면 `작업id`** (선택) — 본문 맨 위에 16~32자 영문·숫자 `작업id` 를 넣으면(예: `{"동작":"신청", "작업id":"a1B2c3D4e5F6g7H8", "신청":{…}}`), 응답을 못 받아 **같은 요청을 다시 보낼 때** 서버가 처음 결과를 그대로 돌려주고 두 번 처리하지 않습니다(24시간). `인증요청`·`신청`·`내예약목록`·`내예약바꾸기` 에 씁니다. 요청마다 새 값을, 다시 보낼 때만 같은 값을 쓰십시오.
- **필드 이름은 한글입니다.** 그대로 보내십시오(`동작`, `휴대폰`, `인증id` …). 바꾸면 인식하지 못합니다. JSON 은 UTF-8.
- **인증·쓰기 키는 없습니다.** 본인 확인은 오직 휴대폰 문자 인증번호입니다. (맥 처리기가 쓰는 운영용 동작은 별도 키가 필요하며 이 문서의 대상이 아닙니다.)
- **처리는 비동기입니다.** 신청·바꾸기는 병원 쪽 처리기가 약 30초 간격으로 가져가 예약판에 적고 결과를 돌려놓습니다. 결과는 2~3초 간격으로 폴링하고, **90초 안에 결과가 없으면** 환자에게 전화(02-871-1936)로 확인하시도록 안내하십시오(홈페이지 화면과 같은 기준).
- 시간대는 모두 **Asia/Seoul(KST)** 입니다. 날짜 `YYYY-MM-DD`, 시각 `HH:MM`(24시간).

---

## 1. 검사 종류와 확정 방식 (Exam types and how a booking is confirmed)

`검사` 값은 아래 여섯 가지 중 하나입니다. 다른 글자는 거절됩니다.

| `검사` 값 | 뜻 (English) | 함께 보낼 것 | 결과 상태 | 확정 방식 |
|---|---|---|---|---|
| `위내시경` | gastroscopy (EGD) | `수면`: `"수면"` 또는 `"비수면"` (필수) | `확정` | **신청 즉시 확정** |
| `대장내시경` | colonoscopy | `대장동의`: `{장약:true, 항응고:true}` (필수) | `확정` | **신청 즉시 확정** — 검사 3일 전까지 장정결제(장을 비우는 약) 수령 |
| `위대장` | gastroscopy + colonoscopy, same day, one sedation | `대장동의` (필수) | `확정` | 같음(즉시 확정 · 장정결제 수령) |
| `맞춤형` | personalized comprehensive check-up (sedated EGD + colonoscopy + 2 ultrasounds + blood panel) | `부위`: 초음파 **정확히 2개** · `대장동의` (필수) | `확정` | 같음(즉시 확정 · 장정결제 수령) |
| `초음파` | ultrasound | `부위`: 1~3개 (4개 이상은 전화) | `확정` | **신청 즉시 확정** |
| `검진` | health check-up | `검진종류`: `"국가검진"` · `"공무원 채용"` · `"일반 채용"` | `확정` | **신청 즉시 확정** |

- `부위` (ultrasound sites) 값: `복부`(abdomen) · `갑상선`(thyroid) · `경동맥`(carotid) · `심장`(heart) · `유방`(breast). 중복 없이 배열로.
- 대장내시경·위대장·맞춤형(대장류)은 **신청하면 바로 확정**되고, 확정 후 병원에서 접수 등록 전화를 한 번 드립니다(환자에게 이 전화를 받아 달라고 전해 주십시오). 다만 장정결제(장을 비우는 약)를 미리 받아 가야 검사할 수 있어서, 신청 전에 환자에게 아래 두 가지를 읽어 주고 **둘 다 동의를 받아** `대장동의: {장약: true, 항응고: true}` 로 보내십시오. 하나라도 빠지면 신청이 거절됩니다(`오류: 대장내시경 준비 안내 두 가지에 동의해 주셔야 예약할 수 있습니다.`).
  - ① 장정결제 수령 — 「대장내시경은 장을 비우는 약을 미리 받아 가셔야 검사할 수 있어요. 검사 3일 전까지 병원에 오셔서 약을 받아 가 주세요. 검사가 3일 안으로 남은 예약은 검사 전날까지 받아 가시면 됩니다. 그때까지 오지 않으시면 예약이 취소될 수 있습니다.」
  - ② 피를 묽게 하는 약 — 「피를 묽게 하는 약(아스피린, 클로피도그렐, 와파린, 항응고제 등)을 드시는 분은 약을 받으러 오실 때 의사와 상담한 뒤 검사 날짜가 바뀔 수 있습니다.」
  - 확정 뒤 병원이 확정 알림톡으로 약 받을 마감일을 보냅니다. 결과 `문구` 에도 마감일이 들어 있으니 그대로 전하십시오. 대장류는 신청 때 `수면` 을 보내지 않습니다 — 수면 검사가 기본이고, 비수면을 원하시면 병원에 오셔서 말씀하시면 됩니다.
- **복부초음파는 금식이 필요합니다.** `부위` 에 `복부` 가 들어가면(`초음파` · `맞춤형`) 신청 전에 환자에게 「복부초음파는 검사 전 8시간 동안 금식해 주세요. 물은 검사 2시간 전까지 드실 수 있습니다.」를 알리고 확인을 받으십시오. 서버로 보내는 칸은 없지만, 홈페이지 화면은 이 확인 체크를 받아야 다음으로 넘어갑니다.
- **시행 의사는 예약에서 지정하지 않습니다.** 여의사(김주희 원장)에게 검사받고 싶다는 환자에게는 「검사 당일 접수 때 말씀해 주세요」라고 안내하십시오. `요청` 칸에 의사 지정을 적지 마십시오.
- 예방접종·보건증·비자 신체검사·면허용 진단서·유학 검진·혈액종합검사 단독은 **예약 없이 방문**하는 항목이라 이 API 에 없습니다.
- 네이버 예약·옛 홈페이지(Wix)에서 한 예약은 이 API 로 바꾸지 못합니다(§5 `바꿈` 참고).

### 신청 값 · 화면 이름 · 빈 시간표 이름 (`표`) — which value goes where
신청의 `검사` 에는 **왼쪽 값만** 씁니다. 가운데 이름(홈페이지 화면·④ 목록의 `검사`)을 신청에 넣으면 `입력` 으로 거절됩니다.

| 신청 `검사` 값 | 홈페이지 화면 · ④ 목록에 보이는 이름 | 보는 빈 시간표 `표` |
|---|---|---|
| `위내시경` | 위내시경 (수면 / 비수면) | `위내시경` — 수면·비수면이 **같은 칸**, 신청 때 `수면` 으로만 구분 |
| `대장내시경` | 대장내시경 | `대장` |
| `위대장` | 위·대장내시경 동시 | `대장` |
| `맞춤형` | 맞춤형 종합검진 (초음파 두 가지) | `대장` |
| `초음파` 1부위 | 초음파 (부위) | `초음파1` (20분) |
| `초음파` 2~3부위 | 초음파 (부위) | `초음파2` (40분) |
| `검진` | 건강검진 (국가검진 · 공무원 채용 · 일반 채용) | `검진` |

---

## 2. ① 빈 시간 보기 — `GET ?표=1`

```
GET https://script.google.com/macros/s/AKfycbzDIeWHIHOkIeO-5pMthBhj5KZVjh-VpmsX_27uIAsiex6-ITbbhgPTlS1ptEwJyVD2/exec?%ED%91%9C=1
```
(`%ED%91%9C` = `표` 를 URL 인코딩한 것.)

> ⚠️ **`표` 키는 반드시 URL 인코딩하십시오.** 인코딩하지 않은 `?표=1` 을 그대로 보내면 Apps Script 까지 가지 못하고 구글 앞단에서 **HTTP 400** HTML 이 옵니다. Python 은 `urllib.parse.urlencode({"표": "1"})`, JavaScript 는 `"?" + new URLSearchParams({표: "1"})`.

응답 (response):
```json
{
  "ok": true,
  "자료": {
    "만든때": "2026-10-13T08:37:24+09:00",
    "기준": "예약판 사본 07:55",
    "표": {
      "위내시경": { "2026-10-14": ["09:20", "09:40", "10:20"], "2026-10-15": ["09:20"] },
      "대장":     { "2026-10-15": ["11:40", "14:40"], "2026-10-17": [] },
      "초음파1":  { "2026-10-14": ["09:20", "09:40"] },
      "초음파2":  { "2026-10-14": ["09:20", "11:40"] },
      "검진":     { "2026-10-14": ["09:40", "09:50"] }
    }
  }
}
```
- `표[표이름][날짜]` 는 그날 신청할 수 있는 **시작 시각 배열**입니다. 빈 배열 `[]` 은 그날 자리가 없다는 뜻, 문자열 `"휴진"` 은 휴진일입니다. **내일부터 30일(약 4주) 분량**이 들어 있고(예: 11월 2일에 받으면 11월 3일~12월 2일, 대장 표는 11월 4일부터), 일요일·공휴일은 없습니다. 기본으로 다섯 표 전체(약 22KB)가 한 번에 옵니다.
- 위 응답 예시의 날짜는 꼴을 보여 주는 예시입니다. 실제 날짜·시각은 받은 표에서 고르십시오.
- `만든때` 는 표를 만든 시각입니다. 병원 쪽이 **10분마다 새로 만들어 올립니다** — `만든때` 는 보통 10분 안팎입니다(2026-11-02 오후까지는 바뀐 게 없으면 30분마다 올려 17~34분 전 표가 보였고, 그날 10분마다로 바꿨습니다). `기준` 은 표의 원자료(예약판 사본)를 받은 시각이라 `만든때` 보다 이를 수 있습니다. **2시간 넘게 낡은 표**로는 서버가 신청을 받지 않습니다(`코드: "전화"`). 신청 직전에 다시 받아 그 시각이 아직 있는지 확인하십시오.
- 오전·오후 구분: 점심시간(13:00~14:00) 전에 시작하는 칸이 오전 진료, 14:00 부터가 오후입니다 — 12시대 칸도 오전 진료입니다. 토요일은 검사 13:00 마감이라 오후 칸이 없습니다.
- 신청은 **내일부터** 가능합니다(오늘은 전화). 대장류는 장정결제 준비 때문에 표가 보통 **이틀 뒤부터** 시작합니다. 당일·지난 날짜는 `마감` 으로 거절됩니다.
- 토요일은 검사 13:00 마감. 표에 있는 시각만 신청하십시오 — 표에 없는 시각은 `마감` 으로 거절됩니다.
- `자료` 가 `null` 이면 지금 표를 보여줄 수 없는 상태입니다. 전화로 안내하십시오.

---

## 3. ② 인증번호 요청 — `동작: "인증요청"`

```json
{ "동작": "인증요청", "휴대폰": "010-1234-5678" }
```
- `휴대폰` 은 환자 본인의 한국 휴대폰 번호. `010` 으로 시작하는 11자리(하이픈은 있어도 없어도 됩니다).
- 성공하면 **환자 휴대폰으로 「[관악수내과의원] 예약 인증번호 ______ (5분 안에 입력해 주세요)」 문자**가 갑니다. 에이전트는 번호를 받을 수 없습니다 — **환자에게 "문자로 받은 6자리를 알려 주세요" 라고 묻고 기다리십시오.**

성공 응답:
```json
{ "ok": true, "인증id": "3f9c…(32자 hex)", "유효초": 300 }
```
- `인증id` 를 보관해 ③ 또는 ④에 함께 보냅니다. **5분(300초) 안에** 써야 하고, 한 번 쓰면 사라집니다.
- 같은 번호로 다시 요청하면 앞의 인증은 무효가 됩니다(살아 있는 인증은 번호당 하나).

실패 응답 (`ok: false`):
| `코드` | 뜻 | 에이전트가 할 일 |
|---|---|---|
| `입력` | 번호 꼴이 틀림 | 번호를 다시 확인 |
| `잦음` | 같은 번호로 10분에 3번 넘게 요청 | 10분 뒤 다시 |
| `진행중` | 이 번호로 처리 중인 신청·요청이 있음 | 먼저 그 결과를 확인(③′·⑤′) |
| `전체` 한도 → `전화` | 병원 전체 요청이 많음(10분 20건·하루 150건·하루 신청 50건) | 잠시 뒤 다시 또는 전화 안내 |
| `바쁨` | 서버 잠금 대기 초과 | 몇 초 뒤 재시도 |
| `오류` | 서버 오류 | 전화 안내 |

응답의 `문구` 는 환자에게 그대로 전해도 되는 한국어 문장입니다.

---

## 4. ③ 예약 신청 — `동작: "신청"` → 결과 폴링 `동작: "결과"`

> **⚠️ 아래 예시를 그대로 복사하지 마십시오. `날짜`·`시각` 은 ①에서 방금 받은 표에서 고른 값으로, `동의: true` 는 환자가 개인정보 수집·이용에 동의한 뒤에만 넣습니다 — 환자 확인 전 `true` 금지.**

```jsonc
{
  "동작": "신청",
  "신청": {
    "인증id": "3f9c…",
    "인증번호": "123456",
    "검사": "위내시경",
    "날짜": "<표에서 고른 날짜 YYYY-MM-DD>",
    "시각": "<그날 배열에 있는 시각 HH:MM>",
    "수면": "수면",
    "부위": [],
    "검진종류": "",
    "이름": "홍길동",
    "휴대폰": "010-1234-5678",
    "생년월일": "1980-01-31",
    "성별": "여",
    "요청": "국가검진도 같이 받고 싶어요",
    "동의": true          // ← 환자가 동의한 뒤에만. 환자 확인 전 true 금지
  }
}
```

| 필드 | 규칙 |
|---|---|
| `인증id` · `인증번호` | ② 응답의 `인증id` 와 환자가 문자로 받은 6자리. 5회 틀리면 인증이 폐기됩니다(`인증만료`) |
| `검사` | §1 의 여섯 값 중 하나 |
| `날짜` · `시각` | ①의 표에 **지금 있는** 날짜·시각 |
| `수면` | `위내시경` 일 때만 `"수면"`/`"비수면"`. 다른 검사는 빈 문자열 또는 생략 |
| `대장동의` | `대장내시경`·`위대장`·`맞춤형` 일 때만 필수. `{"장약": true, "항응고": true}` — §1 의 두 안내를 환자에게 읽어 주고 **환자가 둘 다 동의한 경우에만** 보냅니다. 받은 시각은 서버가 기록합니다 |
| `부위` | `초음파`(1~3개)·`맞춤형`(정확히 2개)일 때만. 다른 검사는 `[]` |
| `검진종류` | `검진` 일 때만 `"국가검진"`/`"공무원 채용"`/`"일반 채용"` |
| `이름` | 한글 또는 영문 2~20자 (patient's name) |
| `휴대폰` | ②에서 인증받은 **그 번호**와 같아야 합니다 |
| `생년월일` | `YYYY-MM-DD` (date of birth) |
| `성별` | `"남"` 또는 `"여"` (sex: male/female) |
| `요청` | 선택. 요청사항(note) **60자까지** — 넘으면 잘립니다. 홈페이지 화면의 「요청사항 (선택)」 칸과 같습니다(예: 「국가검진도 같이 받고 싶어요」). 생년월일·주민번호·전화번호 꼴은 서버가 지웁니다. 의사 지정은 받지 않으니 적지 마십시오 |
| `동의` | 개인정보 수집·이용 동의. **환자가 동의한 경우에만** `true`. 동의 없이 `true` 를 보내면 안 됩니다. 신청의 개인정보는 예약 처리 뒤 바로 서버에서 지워집니다(진료기록은 의료법에 따라 보관) |

**환자에게 읽어 줄 동의 안내** (홈페이지 화면과 같은 문장) — 「개인정보 수집·이용 동의(필수). 모으는 것: 이름, 휴대폰 번호, 생년월일, 성별, 적어 주신 요청사항. 쓰는 곳: 진료 예약과 예약 안내 연락. 보관: 예약 처리 후 바로 삭제합니다(진료기록은 의료법에 따라 보관합니다). 동의하지 않으셔도 됩니다. 그때는 전화로 예약해 주세요.」 환자가 「동의한다」고 답한 경우에만 `동의: true`.

성공 응답: `{ "ok": true, "신청id": "a1b2…(32자 hex)" }` — 이 시점에는 아직 예약이 된 것이 **아닙니다**. 결과를 폴링합니다.

신청을 보냈는데 응답(`신청id`)을 받지 못했다면 **새로 신청하지 마십시오.** `작업id` 를 넣어 보냈다면 같은 본문·같은 `작업id` 로 다시 보내면 같은 `신청id` 가 돌아옵니다. `작업id` 없이 보냈다면 다시 신청하지 말고 ④ 내 예약 목록으로 확인하거나(앞 신청이 아직 처리 중이면 `진행중`), 전화로 확인하도록 안내하십시오.

실패 응답 `코드`:
| `코드` | 뜻 |
|---|---|
| `입력` | 어떤 칸이 틀림 — `문구` 에 어느 칸인지 적혀 있음 |
| `인증만료` | 인증 5분 지남 · 번호 5회 틀림 · 인증받은 번호와 `휴대폰` 이 다름 → ②부터 다시 |
| `인증틀림` | 인증번호가 다름(남은 횟수가 `문구` 에) → 환자에게 번호를 다시 확인 |
| `마감` | 그 시각이 방금 나갔거나 오늘·지난 날짜 → 표를 다시 받아 다른 시각으로 |
| `진행중` | 같은 번호로 처리 중인 신청이 있음 |
| `전화` | 표가 낡음(2시간) · 네 부위 이상 초음파 · 하루 신청 한도(50건) → 전화 안내 |
| `바쁨` · `오류` | 재시도 / 전화 안내 |

### 결과 폴링 — `동작: "결과"`
```json
{ "동작": "결과", "id": "a1b2…" }
```
응답:
| `상태` | 뜻 | 에이전트가 할 일 |
|---|---|---|
| `대기` | 병원 처리기가 아직 적지 않음 | 2~3초 뒤 다시(90초까지) |
| `확정` | **예약 완료** (모든 검사) | `문구` 를 환자에게 전달 — 대장류는 장정결제 받을 마감일이 들어 있습니다. 검사 전 준비는 https://www.gwsooclinic.com/prep |
| `마감` | 적으려는 순간 그 칸이 찼음 | 표를 다시 받아 다른 시각으로 신청 |
| `실패` | 예약되지 않음 (`문구` 에 이유). `바꾸기: true` 가 함께 오면 **같은 번호로 같은 종류의 예약이 이미 있다**는 뜻 | 새로 잡지 말고 §5 「내 예약 바꾸기」로 안내 |
| `없음` | 그런 `id` 가 없음(24시간 지나 지워졌거나 잘못된 id) | — |

같은 휴대폰 번호로 같은 묶음의 검사(예: 위내시경 둘, 대장류 둘, 초음파 둘)는 **두 번 예약할 수 없습니다.** 이미 있으면 `실패` + `바꾸기: true` 로 돌아옵니다.

---

## 5. ④ 내 예약 목록 → ⑤ 옮기기·취소 (My bookings → reschedule / cancel)

### ④ 목록 요청 — `동작: "내예약목록"`
먼저 ②로 인증번호를 받습니다(같은 `인증요청`). 그 뒤:
```json
{ "동작": "내예약목록", "휴대폰": "010-1234-5678", "인증id": "3f9c…", "인증번호": "123456" }
```
성공 응답:
```json
{ "ok": true, "토큰": "c3d4…(32자 hex)", "요청id": "e5f6…", "유효초": 1800 }
```
- `토큰` 은 **30분** 동안 유효한 「내 예약」 세션입니다. 옮기기·취소와 목록 다시 보기에 씁니다. 토큰이 살아 있으면 `{ "동작": "내예약목록", "토큰": "c3d4…" }` 만으로 목록을 다시 요청할 수 있습니다(인증번호 다시 필요 없음).
- 실패 `코드` 는 ③과 같습니다(`입력` · `인증만료` · `인증틀림` · `진행중` · `전화`).

### ④′ 목록 받기 — `동작: "내예약결과"`
```json
{ "동작": "내예약결과", "id": "e5f6…" }
```
`상태: "대기"` 면 2~3초 뒤 다시. 목록이 오면:
```json
{
  "ok": true, "상태": "목록", "문구": "",
  "목록": [
    { "열쇠": "9a8b7c6d5e4f3a2b", "날짜": "2026-10-20", "시각": "09:20", "검사": "위내시경", "이름": "홍OO",
      "표": "위내시경", "바꿈": "가능", "까닭": "" },
    { "열쇠": "…", "날짜": "2026-10-15", "시각": "11:40", "검사": "대장내시경", "이름": "홍OO",
      "표": "", "바꿈": "임박", "까닭": "대장내시경은 장 준비 때문에 검사 2일 전부터는 전화로만 바꿀 수 있습니다. 02-871-1936 으로 전화해 주세요." }
  ]
}
```
- 이 휴대폰 번호로 잡힌 **앞으로 남은** 예약만 옵니다(최대 20건). `이름` 은 첫 글자만 보입니다.
- `열쇠` 는 그 예약을 가리키는 값으로 ⑤에 그대로 보냅니다. 목록을 다시 받으면 바뀔 수 있으니 바로 쓰십시오.
- `표` 는 옮길 때 볼 빈 시간표 이름(§1). 빈 문자열이면 **시간 바꾸기는 전화**, 취소만 가능합니다.

`바꿈` (what the agent may do with this booking):
| `바꿈` | 뜻 | 할 수 있는 것 |
|---|---|---|
| `가능` | 홈페이지·전화로 잡힌 예약 | 옮기기(`표` 가 있을 때)·취소 |
| `임박` | 대장류인데 검사 **2일 전부터** (장정결제 준비 때문) | 온라인 불가 → 전화 02-871-1936 |
| `네이버` | 네이버 예약에서 들어온 예약 | 네이버 예약 화면에서 바꾸기 (https://booking.naver.com/booking/16/bizes/1707149) · 10/27 전에 하신 네이버 예약은 네이버 앱 「내 예약」에서 |
| `전화` | 옛 홈페이지(Wix) 예약 · 출처를 알 수 없는 예약 | 전화 02-871-1936 |

`바꿈` 이 `가능` 이 아닌 예약에 ⑤를 보내면 `코드: "전화"` 와 `까닭` 이 돌아옵니다.

### ⑤ 옮기기·취소 — `동작: "내예약바꾸기"`
옮기기 (reschedule) — 새 시각은 ①의 표에서 `표` 이름에 맞는 것으로 고릅니다:
```json
{ "동작": "내예약바꾸기", "토큰": "c3d4…", "열쇠": "9a8b7c6d5e4f3a2b", "무엇": "옮기기", "새날짜": "<표에서 고른 날짜>", "새시각": "<그날 배열에 있는 시각>" }
```
취소 (cancel) — `사유` 는 세 가지 중 하나:
```json
{ "동작": "내예약바꾸기", "토큰": "c3d4…", "열쇠": "9a8b7c6d5e4f3a2b", "무엇": "취소", "사유": "일정 변경" }
```
`사유` 값: `"일정 변경"` · `"개인 사정"` · `"기타"`.

성공 응답: `{ "ok": true, "요청id": "…" }` → ⑤′로 폴링.

실패 `코드`:
| `코드` | 뜻 |
|---|---|
| `인증만료` | 토큰 30분 지남 → ②·④부터 다시 |
| `없음` | 그 `열쇠` 가 목록에 없음 → 목록 다시 받기 |
| `전화` | `바꿈` 이 `가능` 이 아님 · `표` 없는 예약의 옮기기 · 토큰당 바꾸기 5회 초과 · 하루 한도 → `문구`/`까닭` 대로 안내 |
| `입력` | `무엇`·`새날짜`·`새시각`·`사유` 가 틀림, 또는 지금과 같은 시각 |
| `마감` | 새 시각이 표에 없음 · 오늘/지난 날짜 → 다른 시각 |
| `진행중` | 같은 번호로 처리 중인 요청 있음 |

### ⑤′ 결과 — `동작: "내예약결과"`
| `상태` | 뜻 |
|---|---|
| `대기` | 처리 중 → 2~3초 뒤 다시(90초까지) |
| `완료` | **옮기기 또는 취소가 반영됨** (`문구` 에 「…으로 바꿨습니다」/「…예약을 취소했습니다」) |
| `마감` | 새 시각이 그사이 찼음 · 대장류는 새 날짜도 **2일 이상 뒤**여야 함 → 다른 시각으로 다시 |
| `실패` | 반영되지 않음(`문구` 에 이유). `네이버: true` 가 함께 오면 네이버 예약 화면으로 안내 |
| `없음` | 그런 `id` 가 없음 |

옮기기가 완료되면 병원이 새 시각으로 예약 확인 알림톡을 보냅니다(위내시경 제외). 취소는 알림이 따로 가지 않습니다.

---

## 6. 한도·시간 (Limits)

| 항목 | 값 |
|---|---|
| 인증번호 유효 | 300초(5분), 틀림 5회까지, 1회용 |
| 인증 요청 | 같은 번호 10분에 3회 · 전체 10분에 20회 · 하루 150회 |
| 예약 신청 | 하루 50건(병원 전체) · 같은 번호 같은 검사 묶음은 1건 |
| 내 예약 토큰 | 30분 · 토큰당 옮기기·취소 5회 |
| 빈 시간표 | 내일부터 30일 · 10분마다 새로 올림 · 2시간 넘게 낡으면 신청 거절 |
| 결과 보관 | 24시간(그 뒤 `없음`) |
| 처리 주기 | 병원 처리기 약 30초 · 결과 폴링 2~3초 · 90초 넘으면 전화 안내 |
| 한 번에 처리 중인 요청 | 같은 번호로 1건(끝나기 전에는 `진행중`) |
| 대장류 변경 | 검사 2일 전부터 온라인 불가(전화) |

---

## 7. 에이전트 규칙 (Rules for agents)

1. **본인 또는 환자가 명시적으로 맡긴 예약만** 다룹니다. 환자가 「내 예약 잡아 줘 / 옮겨 줘 / 취소해 줘」라고 한 그 사람의 휴대폰으로만 인증을 요청하십시오. 다른 사람 명의·번호로 시험하거나 대신 잡지 않습니다.
2. **인증번호는 환자가 알려 준 것만** 씁니다. 추측·무작위 시도·재사용을 하지 않습니다. 환자가 번호를 모르면 「문자를 확인해 주세요」 하고 기다리거나, 5분이 지났으면 다시 요청합니다.
3. **의료 판단을 하지 않습니다.** 어떤 검사를 받아야 하는지, 수면을 할지, 약을 끊을지, 금식을 어떻게 할지는 안내 페이지를 소개하거나 병원에 묻도록 합니다(검사 전 준비 https://www.gwsooclinic.com/prep · 자주 묻는 질문 https://www.gwsooclinic.com/faq). 대장류는 수면 검사가 기본이고, 비수면을 원하시면 병원에 오셔서 말씀하시면 됩니다.
4. **신청 전에 환자에게 확인**합니다 — 검사·날짜·시각·이름·생년월일·휴대폰, 그리고 개인정보 수집·이용 동의(§4 의 안내문). `동의: true` 는 환자가 동의했을 때만 보냅니다. 대장류는 §1 의 두 안내, 복부초음파는 금식 안내도 함께 확인합니다.
5. **결과 상태를 그대로 전합니다.** `확정` 은 예약 완료입니다 — 대장류는 `문구` 의 장정결제 마감일까지 함께 전합니다. `실패`·`늦음`(90초 초과)은 「예약이 되지 않았을 수 있다」고 하고 전화 확인을 권합니다.
6. **중복 예약을 만들지 않습니다.** `바꾸기: true` 가 오면 새로 잡지 말고 「내 예약 바꾸기」로 옮깁니다. 이미 예약이 있는지 모르면 먼저 ④로 목록을 봅니다.
7. **막히면 전화 02-871-1936** (월~금 09:00~18:00, 토 09:00~14:00). `코드: "전화"` · `바꿈: "전화"/"임박"` · `실패` 는 전화로 안내합니다. 네이버에서 한 예약은 네이버 예약 화면에서 바꿉니다.
8. **빈 시간표를 긁어 저장하거나 반복 조회로 서버를 두드리지 않습니다.** 필요할 때 한 번 받고, 신청 직전에 한 번 더 받으면 충분합니다.

---

## 8. 흐름 예시 (Example, pseudo-code)

```
API = "https://script.google.com/macros/s/AKfycbzDIeWHIHOkIeO-5pMthBhj5KZVjh-VpmsX_27uIAsiex6-ITbbhgPTlS1ptEwJyVD2/exec"

slots = GET(API + "?%ED%91%9C=1").자료.표["위내시경"]    # ① 표 키는 인코딩. 환자와 날짜·시각을 고른다
r = POST(API, {동작:"인증요청", 휴대폰:phone})           # ② 환자 휴대폰으로 문자
code = ask_patient("문자로 받은 6자리 인증번호를 알려 주세요")
consent = ask_patient(§4 동의 안내)                       # 동의해야만 아래 동의:true
r = POST(API, {동작:"신청", 작업id:new_id(), 신청:{인증id:r.인증id, 인증번호:code, 검사:"위내시경", 날짜:d, 시각:t, 수면:"수면",
                               이름:name, 휴대폰:phone, 생년월일:dob, 성별:sex, 요청:note, 동의:consent}})   # ③ d·t 는 방금 받은 표에서
loop up to 90s every 3s:
    x = POST(API, {동작:"결과", id:r.신청id})
    if x.상태 != "대기": break
tell_patient(x.상태, x.문구)                              # 확정 / 마감 / 실패
```

curl 로 하면 (`-X POST` 를 붙이지 마십시오 — §0):
```bash
API="https://script.google.com/macros/s/AKfycbzDIeWHIHOkIeO-5pMthBhj5KZVjh-VpmsX_27uIAsiex6-ITbbhgPTlS1ptEwJyVD2/exec"
curl -sL "$API?%ED%91%9C=1"                                                     # ① 빈 시간
curl -sL -H "Content-Type: text/plain;charset=utf-8" \
     -d '{"동작":"인증요청","휴대폰":"<환자 휴대폰>"}' "$API"                     # ② 환자 휴대폰으로 문자
curl -sL -H "Content-Type: text/plain;charset=utf-8" \
     -d '{"동작":"결과","id":"<신청id>"}' "$API"                                # ③′ 결과
```

문의: 관악수내과의원 02-871-1936 · https://www.gwsooclinic.com · 서울 관악구 남부순환로 1810 2층(서울대입구역 4번 출구).
