นโยบายความเป็นส่วนตัว — Siriraj Rx Shift (Google Calendar Sync)
อัปเดตล่าสุด: 3 สิงหาคม 2569 (2026-08-03) · English version below
เอกสารนี้ครอบคลุมเฉพาะฟีเจอร์ Google Calendar Sync ของระบบ Siriraj Rx Shift ("ระบบ", "เรา") ซึ่งเป็นระบบจัดตารางเวรภายในของฝ่ายเภสัชกรรม โรงพยาบาลศิริราช (ระบบหลักใช้ชื่อ + รหัสผ่านภายใน ไม่ผ่านบัญชี Google และไม่ได้เข้าถึงข้อมูล Google ของผู้ใช้)
1. ภาพรวม
ฟีเจอร์ Google Calendar Sync เป็นทางเลือกเสริมแบบ opt-in ที่ช่วยให้ผู้ใช้เห็นเวรของตัวเอง ในปฏิทิน Google ส่วนตัวโดยอัตโนมัติ โดยไม่ต้องคัดลอกด้วยมือ
ผู้ใช้ที่ไม่กดเปิดฟีเจอร์นี้ จะไม่ถูกขอสิทธิ์ Google ใดๆ ทั้งสิ้น และใช้ระบบได้ตามปกติครบทุกฟังก์ชัน
2. สิทธิ์ (OAuth scope) ที่เราขอ และใช้ทำอะไร
ฟีเจอร์นี้ทำงานบน Google Apps Script โปรเจกต์แยกต่างหาก (calendar-sync-app) ซึ่งขอสิทธิ์
เพียง 4 รายการนี้เท่านั้น — ตรงกับที่ประกาศไว้ใน manifest ของแอปทุกตัว ไม่มีมากกว่านี้
| Scope | ใช้ทำอะไร | ขอบเขตที่จำกัดไว้ |
|---|---|---|
| .../auth/calendar | สร้าง / แก้ไข / ลบ event ที่แทนเวรของผู้ใช้เอง และให้ผู้ใช้เลือกหรือสร้างปฏิทินปลายทางได้ (เช่น สร้างปฏิทินแยกชื่อ "Siriraj Rx Shifts" เพื่อไม่ให้ปนกับปฏิทินส่วนตัว) | แก้ไข/ลบเฉพาะ event ที่ระบบนี้สร้างเอง — อ้างอิงจากตาราง "เวร ↔ event ID" ที่เก็บในพื้นที่ส่วนตัวของผู้ใช้ · ไม่นำเนื้อหา event ใดของผู้ใช้ไปเก็บหรือส่งออก · ไม่เข้าถึงปฏิทินของผู้อื่น |
| .../auth/script.scriptapp | สร้าง / ลบ time-based trigger ในบัญชีของผู้ใช้เอง เพื่อให้ปฏิทินอัปเดตตามเวรล่าสุด แม้ผู้ใช้ปิดแอปอยู่ | ติดตั้ง trigger ได้ตัวเดียว (_autoSyncTick) · ผู้ใช้เปิด/ปิดเองได้ตลอด ·
ไม่ให้สิทธิ์เข้าถึงบริการ Google อื่นเพิ่ม |
| .../auth/script.external_request | เรียก HTTPS ออกไปยังฐานข้อมูลของเราเอง (Firebase Realtime Database) เพื่ออ่าน "เวรล่าสุดของผู้ใช้คนนี้" และเขียนใบเสร็จยืนยันว่าซิงค์สำเร็จ | ปลายทางเดียวคือ siriraj-rx-shift-default-rtdb.asia-southeast1.firebasedatabase.app ·
ไม่มีบริการบุคคลที่สามเกี่ยวข้อง · ไม่ส่งข้อมูล Google ของผู้ใช้ออกไป |
| .../auth/userinfo.email | อ่านอีเมลของบัญชี Google ที่ผู้ใช้เลือก เพื่อแสดงให้เห็นว่ากำลังจะซิงค์เข้าบัญชีไหน และกันการซิงค์ผิดบัญชี | ใช้แสดงผลและจับคู่บัญชีเท่านั้น · ไม่ส่งอีเมลออกนอกระบบ · ไม่ใช้ทำการตลาด |
ทำไมต้องใช้ calendar ไม่ใช่ calendar.events ที่แคบกว่า:
เพราะฟีเจอร์นี้ให้ผู้ใช้ เลือกปฏิทินปลายทางหรือสร้างปฏิทินใหม่แยกต่างหาก ได้
ซึ่งเป็นการจัดการตัวปฏิทิน ไม่ใช่แค่ event — calendar.events ทำไม่ได้
ทางเลือกเดียวถ้าใช้ scope แคบกว่านี้คือบังคับเขียนลงปฏิทินหลักของผู้ใช้เสมอ
ซึ่งเป็นผลเสียต่อความเป็นส่วนตัวของผู้ใช้เองมากกว่า (ปนกับปฏิทินส่วนตัว ปิด/ซ่อนแยกไม่ได้)
3. ข้อมูลที่เราเข้าถึง เก็บ และเก็บไว้ที่ไหน
| ข้อมูล | เก็บที่ไหน | ใครเข้าถึงได้ |
|---|---|---|
| OAuth access / refresh token | เราไม่เก็บเลย — Google Apps Script เป็นผู้ถือและจัดการทั้งหมดในโครงสร้างพื้นฐานของ Google เราไม่มีสำเนา ไม่มีที่เก็บ ไม่มีทางส่งต่อ | ไม่มีใครในฝั่งเรา |
| Mapping "เวร ↔ event ID" และปฏิทินปลายทางที่เลือก | PropertiesService.getUserProperties() — พื้นที่เก็บข้อมูลส่วนตัวของผู้ใช้แต่ละคน
ที่ Google จัดสรรให้ ผูกกับบัญชีผู้ใช้คนนั้นโดยตรง |
เฉพาะการรันสคริปต์ในนามของผู้ใช้คนนั้นเอง — ผู้พัฒนาเปิดดูไม่ได้ ผู้ใช้คนอื่นเข้าไม่ถึง แม้แต่ผู้ดูแลระบบก็อ่านไม่ได้ |
| อีเมลบัญชี Google ที่เลือก | หน่วยความจำชั่วคราวระหว่างรัน + PropertiesService ของผู้ใช้เอง |
เช่นเดียวกับข้างบน |
| รายการเวรของผู้ใช้ (feed) | Firebase Realtime Database region asia-southeast1 — เขียนโดยระบบหลัก
อ่านโดย trigger ของผู้ใช้ |
ดูข้อ 5.6 |
| ใบเสร็จยืนยันการซิงค์ (ack) | Firebase เช่นกัน — เก็บแค่จำนวน event ที่สร้าง/แก้/ลบ และเวลา ไม่มีเนื้อหา event | ดูข้อ 5.6 |
สิ่งที่ปรากฏใน calendar event: วันที่ · เวลาเริ่ม-จบ · ประเภทเวร (เช่น "เวรเช้า") · หน่วยงาน/ห้องที่ประจำ เท่านั้น
สิ่งที่ไม่มีวันเกี่ยวข้อง: ข้อมูลผู้ป่วย · ข้อมูลสุขภาพ (PHI/PHR) · เลข HN · ข้อมูลเวชระเบียน — ระบบทั้งระบบเป็นระบบจัดเวรบุคลากร ไม่ใช่ระบบเวชระเบียน จึงไม่เคยแตะข้อมูลเหล่านี้เลยตั้งแต่ต้น
ทิศทางการไหลของข้อมูลเป็นทางเดียว: ระบบ → ปฏิทินของผู้ใช้ — เราไม่แตะต้อง event ที่ระบบไม่ได้สร้าง ไม่เข้าถึงปฏิทินอื่นที่ผู้ใช้ไม่ได้เลือก และไม่ดึงเนื้อหาใดจากปฏิทินกลับเข้าระบบ — ระหว่างซิงค์ ระบบดูรายการ event เฉพาะในช่วงวันที่ของชุดเวรบนปฏิทินปลายทาง เพียงเพื่อยืนยันว่า event ที่ระบบสร้างไว้ยังอยู่ครบ (ตรวจเฉพาะ event ID กับช่อง location) ไม่อ่านเนื้อหา และไม่มีข้อมูลของ event อื่นถูกเก็บหรือส่งออก
4. วัตถุประสงค์การใช้ข้อมูล และ Limited Use
ใช้เพื่อวัตถุประสงค์เดียวคือ เตือนความจำเวรทำงาน ของผู้ใช้ผ่านปฏิทินที่ผู้ใช้ใช้อยู่แล้ว
การใช้งานและถ่ายโอนข้อมูลที่ได้รับจาก Google API เป็นไปตาม Google API Services User Data Policy รวมถึงข้อกำหนด Limited Use โดยเฉพาะ เราขอยืนยันว่า:
- ไม่ใช้ข้อมูลเพื่อการโฆษณาทุกรูปแบบ
- ไม่ขาย ไม่ให้เช่า ไม่แลกเปลี่ยนข้อมูล
- ไม่โอนข้อมูลให้บุคคลที่สาม เว้นแต่ (ก) ได้รับความยินยอมโดยชัดแจ้งจากผู้ใช้ (ข) เพื่อความมั่นคงปลอดภัย หรือ (ค) เมื่อกฎหมายบังคับ
- ไม่ให้มนุษย์อ่านข้อมูลผู้ใช้ เว้นแต่ (ก) ผู้ใช้ยินยอมโดยชัดแจ้งเพื่อแก้ปัญหาเฉพาะราย (ข) เพื่อความมั่นคงปลอดภัย หรือ (ค) เมื่อกฎหมายบังคับ
- ไม่ใช้ข้อมูลจาก Google Workspace API เพื่อพัฒนา ฝึกฝน หรือปรับปรุงโมเดล AI/ML ใดๆ ทั้งของเราเองและของบุคคลที่สาม — ระบบนี้ไม่มีการเรียกใช้บริการ AI/ML ใดๆ เลย
5. มาตรการคุ้มครองข้อมูล (Data Protection)
5.1 การเข้ารหัสระหว่างรับส่ง (in transit)
การรับส่งข้อมูลทุกเส้นทางบังคับใช้ HTTPS/TLS 1.2 ขึ้นไป ไม่มีช่องทางที่เป็น HTTP ธรรมดา:
เบราว์เซอร์ผู้ใช้ ↔ Apps Script web app · Apps Script ↔ Google Calendar API ·
Apps Script ↔ Firebase Realtime Database (UrlFetchApp ปฏิเสธ certificate ที่ไม่ถูกต้องโดยอัตโนมัติ)
5.2 การเข้ารหัสขณะจัดเก็บ (at rest)
ข้อมูลทั้งหมดอยู่ในโครงสร้างพื้นฐานของ Google (Apps Script PropertiesService, Google Sheets,
Firebase Realtime Database) ซึ่ง เข้ารหัสขณะจัดเก็บด้วย AES-256 โดยค่าเริ่มต้น
เราไม่มีเซิร์ฟเวอร์ ไม่มีฐานข้อมูล และไม่มีดิสก์ของตัวเองที่เก็บข้อมูลผู้ใช้เลยแม้แต่จุดเดียว
5.3 การจำกัดสิทธิ์เข้าถึง — ผู้พัฒนาเข้าถึงข้อมูล Google ของผู้ใช้ไม่ได้เลย
นี่คือมาตรการที่แข็งแรงที่สุดของสถาปัตยกรรมนี้ และเป็นผลจากการออกแบบโดยตั้งใจ:
- แอปทำงานในโหมด "execute as user accessing" — ทุกการเรียก Calendar API รันในนามของผู้ใช้คนนั้น ด้วยสิทธิ์ของผู้ใช้คนนั้น ไม่ใช่ในนามผู้พัฒนา
- ไม่มี service account · ไม่มี domain-wide delegation · ไม่มี API key ฝั่งเซิร์ฟเวอร์ ที่ให้ผู้พัฒนาเข้าถึงปฏิทินของใครได้
- Mapping และการตั้งค่าเก็บใน
PropertiesService.getUserProperties()ซึ่ง Google แยกพื้นที่ให้รายบุคคล — ผู้พัฒนาเปิด Apps Script editor ก็อ่านของผู้ใช้คนอื่นไม่ได้ ไม่มี API ให้ทำ - ผลลัพธ์: แม้บัญชีผู้พัฒนาถูกยึด ผู้โจมตีก็ยังอ่านปฏิทินหรือ mapping ของผู้ใช้ไม่ได้
5.4 การจัดการ token
เราไม่เก็บ ไม่บันทึก (log) และไม่ส่งต่อ OAuth token ใดๆ ทั้ง access token และ refresh token อยู่ในความดูแลของ Google Apps Script ทั้งหมด เมื่อผู้ใช้ถอนสิทธิ์ที่ Google Account permissions สิทธิ์จะหมดผลทันทีโดยไม่ต้องรอให้ฝั่งเราทำอะไร
5.5 การแยกโปรเจกต์เพื่อจำกัดความเสียหาย (least privilege by isolation)
ฟีเจอร์ Calendar ถูกแยกออกเป็น Apps Script โปรเจกต์และ Google Cloud project ต่างหาก จากระบบหลักโดยเจตนา ระบบหลัก (ที่มีสิทธิ์ระดับสูงกว่าสำหรับงานหลังบ้านของผู้ดูแล) และโปรเจกต์ Calendar ไม่แชร์ credential กันเลย ผู้ใช้ที่กดเชื่อมต่อปฏิทินจึงถูกขอสิทธิ์เฉพาะ 4 รายการในข้อ 2 เท่านั้น ไม่มีสิทธิ์ของระบบหลังบ้านปนเข้ามาแม้แต่ตัวเดียว
5.6 ข้อมูลตารางเวรใน Firebase (feed / ack)
calFeed เก็บรายการเวรของผู้ใช้ ระบุด้วยชื่อเรียกภายในหน่วยงาน
(ไม่ใช่อีเมล ไม่ใช่ Google ID) เนื้อหาคือข้อมูลชุดเดียวกับตารางเวรกลางที่บุคลากรในหน่วยงานทุกคน
เห็นอยู่แล้วเป็นปกติ ส่วน calAck เก็บเพียงตัวเลขสรุป (สร้าง/แก้/ลบ กี่รายการ เมื่อไหร่)
ไม่มีเนื้อหา event
ฐานข้อมูลนี้อยู่ใน region asia-southeast1 (สิงคโปร์) เข้ารหัสขณะจัดเก็บ และ
ไม่มีข้อมูลที่ได้จาก Google API ของผู้ใช้เก็บอยู่เลย — ไม่มี token ไม่มี event ID
ไม่มีเนื้อหาปฏิทิน
5.7 การบันทึกและตรวจสอบย้อนหลัง
การรันสคริปต์ทุกครั้งถูกบันทึกโดยอัตโนมัติใน Google Cloud Logging (Stackdriver)
ตามค่าที่ตั้งไว้ในโปรเจกต์ และการซิงค์แต่ละรอบเขียน "ใบเสร็จ" (calAck) ที่ระบุเวลาและจำนวน
event ที่สร้าง/แก้/ลบ เพื่อให้ตรวจย้อนได้ว่าปฏิทินถูกแก้ไปกี่รายการเมื่อไหร่
บันทึกเหล่านี้ ไม่บันทึกเนื้อหา event และไม่บันทึก token ใดๆ
5.8 ระยะเวลาเก็บรักษาและการลบ
| ข้อมูล | เก็บนานเท่าไร |
|---|---|
| Mapping เวร ↔ event ID | ตราบเท่าที่ผู้ใช้ยังเปิดฟีเจอร์นี้ · ลบทันทีเมื่อผู้ใช้กด "ยกเลิกการเชื่อมต่อ" |
| อีเมลบัญชีที่ผูกไว้ | เช่นเดียวกัน |
calFeed ของผู้ใช้ | เก็บเวรรุ่นล่าสุดของแต่ละเดือนที่เผยแพร่ · เมื่อเวรเดือนใดเปลี่ยน ข้อมูลเดือนนั้นถูกเขียนทับทั้งก้อน ไม่เก็บประวัติการแก้ไข |
calAck (ใบเสร็จ) | เก็บเฉพาะรายการล่าสุด · ถูกเขียนทับทุกครั้งที่ซิงค์ |
เมื่อผู้ใช้กด "ยกเลิกการเชื่อมต่อ" ระบบจะ (1) ลบ event ที่ระบบสร้างไว้ทั้งหมด
ออกจากปฏิทินผู้ใช้ (2) ลบ mapping และการตั้งค่าออกจาก PropertiesService
(3) ลบ trigger ที่ติดตั้งไว้ และ (4) เพิกถอน authorization ของสคริปต์ — ทั้งหมดนี้จบภายในการกดปุ่มครั้งเดียว
(หาก event ใดลบไม่สำเร็จในจังหวะนั้น เช่น เข้าถึงปฏิทินไม่ได้ชั่วคราว ระบบจะแจ้งผู้ใช้
และเก็บ mapping เฉพาะรายการนั้นไว้เพื่อตามลบให้สำเร็จในครั้งถัดไป ไม่ปล่อยเป็น event ค้าง)
5.9 การรับมือเหตุละเมิดข้อมูล
หากพบเหตุที่กระทบต่อข้อมูลผู้ใช้ เราจะ (1) ระงับฟีเจอร์และเพิกถอน authorization ทันที (2) แจ้งผู้ใช้ที่ได้รับผลกระทบทางอีเมลและผ่านประกาศในระบบ ภายใน 72 ชั่วโมง นับจากเวลาที่ทราบเหตุ และ (3) แจ้ง Google ผ่านช่องทางที่กำหนด หากเหตุนั้นเกี่ยวข้องกับข้อมูลจาก Google API
5.10 บุคลากรที่เข้าถึงได้
ระบบนี้พัฒนาและดูแลโดย ผู้พัฒนาเพียงคนเดียว (ผู้ติดต่อในข้อ 9) ไม่มีพนักงาน ไม่มีผู้รับจ้างช่วง ไม่มีบุคคลที่สามที่ได้รับสิทธิ์เข้าถึง บัญชี Google ของผู้พัฒนาที่เป็นเจ้าของโปรเจกต์ เปิดใช้ การยืนยันตัวตนสองชั้น (2-Step Verification) อยู่
6. การแชร์ข้อมูลกับบุคคลที่สาม
เราไม่แชร์ข้อมูลใดๆ ที่ได้จาก Google API กับบุคคลที่สามรายใดทั้งสิ้น ไม่มีบริการวิเคราะห์ (analytics) ไม่มี tracker ไม่มีโฆษณา ไม่มี CDN ภายนอกที่รับข้อมูลผู้ใช้ ผู้ประมวลผลข้อมูลรายเดียวคือ Google LLC ในฐานะผู้ให้บริการโครงสร้างพื้นฐาน (Apps Script, Calendar, Firebase)
7. สิทธิ์ของผู้ใช้
ผู้ใช้มีสิทธิ์เข้าถึง แก้ไข ขอสำเนา คัดค้านการประมวลผล และขอลบข้อมูลของตน ตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 (PDPA) โดยดำเนินการได้เองทันทีดังนี้:
- ยกเลิกการเชื่อมต่อในแอป — กดปุ่ม "ยกเลิกการเชื่อมต่อ" ในหน้า "ตั้งค่าปฏิทินเวร" ระบบจะลบ event ทั้งหมดที่สร้างไว้ + ลบข้อมูลที่เก็บไว้ + ลบ trigger ให้อัตโนมัติ
- ลบเฉพาะ event โดยไม่ตัดการเชื่อมต่อ — กดปุ่ม "ล้าง event เวรทั้งหมด"
- ถอนสิทธิ์จากฝั่ง Google โดยตรง — ที่ Google Account permissions เลือกแอป "Siriraj Rx Shift Calendar" แล้วกดยกเลิก
- ปิดการอัปเดตอัตโนมัติอย่างเดียว — สลับสวิตช์ "อัปเดตอัตโนมัติ" (trigger จะถูกลบ · event ที่ซิงค์ไว้ยังอยู่)
คำขอลบข้อมูลหรือคำถามอื่นๆ ส่งได้ที่อีเมลในข้อ 9 เราจะดำเนินการภายใน 30 วัน
8. เด็กและเยาวชน
ระบบนี้ใช้งานโดยบุคลากรวิชาชีพเภสัชกรรมเท่านั้น ไม่ได้ออกแบบหรือมุ่งหมายให้ผู้มีอายุต่ำกว่า 18 ปีใช้งาน และเราไม่เก็บข้อมูลจากผู้เยาว์โดยเจตนา
9. การเปลี่ยนแปลงนโยบายนี้ และการติดต่อ
หากมีการเปลี่ยนแปลงสาระสำคัญ เราจะปรับวันที่ "อัปเดตล่าสุด" ด้านบน และแจ้งผู้ใช้ผ่านประกาศในระบบ
ผู้ควบคุมข้อมูล / ผู้ติดต่อ: Norapol Uttho —
norapol.uttho@gmail.com
เว็บไซต์แอป: https://sirxshift.com/
Privacy Policy — Siriraj Rx Shift (Google Calendar Sync)
Last updated: 3 August 2026 · ดูฉบับภาษาไทยด้านบน
This is a complete English translation of the Thai text above, provided for reviewer convenience. In case of discrepancy, the Thai text is authoritative.
1. Overview
Siriraj Rx Shift ("the app", "we") is an internal duty-roster system for the Pharmacy Department, Siriraj Hospital, Bangkok, Thailand. The Google Calendar Sync feature is an optional, opt-in add-on that mirrors a user's own duty shifts into their personal Google Calendar so they do not have to copy them by hand.
Users who do not enable this feature are never asked for any Google permission and retain full use of the application.
2. OAuth scopes we request, and why
The feature runs in a separate Google Apps Script project and separate Google Cloud
project (calendar-sync-app) which requests only these four
scopes — exactly matching the app manifest, with nothing additional:
| Scope | What it is used for | How it is constrained |
|---|---|---|
| .../auth/calendar | Create, update, and delete events representing the signed-in user's own shifts, and let the user choose or create the destination calendar (e.g. a separate "Siriraj Rx Shifts" calendar so work shifts do not clutter their personal calendar) | Only modifies or deletes events this app created — identified from the "shift ↔ event ID" table held in that user's own private storage · Never stores or transmits the content of any user event · Never accesses another user's calendar |
| .../auth/script.scriptapp | Create and delete a time-based trigger in the user's own execution context, so the calendar keeps up with roster changes while the app is closed | Exactly one trigger (_autoSyncTick) · User can enable/disable it at any time ·
Grants no access to any additional Google service |
| .../auth/script.external_request | Outbound HTTPS to our own Firebase Realtime Database to read "this user's latest shifts" and write a small sync-acknowledgement receipt | Single destination:
siriraj-rx-shift-default-rtdb.asia-southeast1.firebasedatabase.app ·
No third-party service involved · No Google user data is ever sent out |
| .../auth/userinfo.email | Read the email address of the Google account the user selected, so we can show which account will receive the shifts and prevent syncing into the wrong account | Display and account-matching only · Never transmitted outside the system · Never used for marketing |
Why calendar and not the narrower calendar.events:
the feature lets the user pick an existing destination calendar or create a new dedicated
one. That is calendar-level management (calendar list and calendar objects), which
calendar.events cannot do. The only way to use the narrower scope would be to always
write into the user's primary calendar — which is worse for user privacy, because work
shifts would then be inseparable from personal events and could not be hidden or unsubscribed
independently.
3. What we access, what we store, and where
| Data | Where it lives | Who can reach it |
|---|---|---|
| OAuth access / refresh tokens | We never store them. Google Apps Script holds and manages them entirely within Google infrastructure. We hold no copy and have no storage for them. | Nobody on our side |
| Shift ↔ event ID mapping, chosen destination calendar | PropertiesService.getUserProperties() — per-user private storage allocated by
Google and bound to that user's account |
Only script executions running as that user. The developer cannot read it; other users cannot reach it; no administrator API exposes it |
| Selected Google account email | Transient memory during execution, plus that user's own PropertiesService |
Same as above |
| The user's shift list (feed) | Firebase Realtime Database, region asia-southeast1 — written by the main system,
read by the user's own trigger |
See §5.6 |
| Sync acknowledgement (ack) | Firebase, same database — counts of events created/updated/deleted plus a timestamp; no event content | See §5.6 |
What appears in a calendar event: date · start and end time · shift type (e.g. "morning shift") · assigned unit or room. Nothing else.
What is never involved: patient data · health information (PHI/PHR) · medical record numbers · clinical records. The system is a staff scheduling tool, not a medical records system, and has never touched such data.
Data flow is one-way: app → the user's calendar. We do not touch events the app did not create, do not access calendars the user did not select, and do not pull any calendar content back into the system. During a sync, the app lists events only within the shift date range on the destination calendar, solely to verify that its own previously created events are still present — it checks only the event ID and the location field, reads no event content, and stores or transmits nothing from any other event.
4. Purpose of use, and Limited Use compliance
The data is used for exactly one purpose: reminding the user of their own work shifts in the calendar they already use daily.
Our use and transfer of information received from Google APIs adheres to the Google API Services User Data Policy, including the Limited Use requirements. Specifically we affirm that we do not:
- use the data for advertising of any kind;
- sell, rent, or trade the data;
- transfer the data to third parties, except (a) with the user's explicit consent, (b) for security purposes, or (c) to comply with applicable law;
- allow humans to read the data, except (a) with the user's explicit consent for a specific support issue, (b) for security purposes, or (c) to comply with applicable law;
- use Google Workspace API data to develop, train, or improve any generalized or non-personalized artificial intelligence or machine learning model, whether our own or a third party's. This application makes no AI/ML calls of any kind.
5. Data protection measures
5.1 Encryption in transit
Every network path enforces HTTPS/TLS 1.2 or higher; no plain-HTTP path exists
anywhere: user's browser ↔ Apps Script web app · Apps Script ↔ Google Calendar API ·
Apps Script ↔ Firebase Realtime Database (UrlFetchApp rejects invalid certificates
automatically).
5.2 Encryption at rest
All data resides in Google infrastructure (Apps Script PropertiesService, Google
Sheets, Firebase Realtime Database), which is encrypted at rest with AES-256 by
default. We operate no servers, no databases, and no disks of our own that hold user data
at any point.
5.3 Access control — the developer cannot reach users' Google data at all
This is the strongest protection in the design, and it is deliberate:
- The app runs as "execute as user accessing". Every Calendar API call executes as that user, with that user's own credentials — never as the developer.
- There is no service account, no domain-wide delegation, and no server-side API key that would let the developer reach anyone's calendar.
- Mappings and settings live in
PropertiesService.getUserProperties(), which Google partitions per user. Opening the Apps Script editor as the developer does not expose another user's properties; no API exists to do so. - Consequence: even if the developer's account were compromised, the attacker still could not read any user's calendar or mapping data.
5.4 Token handling
We do not store, log, or transmit any OAuth token. Access and refresh tokens are held entirely by Google Apps Script. When a user revokes access at Google Account permissions, authorization ends immediately without requiring any action on our side.
5.5 Project isolation for least privilege
The Calendar feature was deliberately split into its own Apps Script project and its own Google Cloud project, separate from the main system. The main system (which holds higher-privilege scopes used only for administrator back-office tasks) and the Calendar project share no credentials whatsoever. A user connecting their calendar is therefore asked for exactly the four scopes in §2 and nothing from the back-office side.
5.6 Roster data in Firebase (feed / ack)
calFeed holds a user's shift list keyed by their internal display
name (not an email address, not a Google ID). Its contents are the same roster information
that every member of the department already sees on the shared duty roster as a
matter of normal operation. calAck holds only summary counts (how many events were
created/updated/deleted, and when) — no event content.
The database is in region asia-southeast1 (Singapore), encrypted at rest, and holds
no data obtained from Google APIs whatsoever — no tokens, no event IDs, no calendar
content.
5.7 Logging and auditability
Every script execution is recorded automatically in Google Cloud Logging
(Stackdriver), as configured for the project, and each sync run writes an acknowledgement
record (calAck) with a timestamp and the number of events created, updated, and
deleted — so it is always auditable how many calendar changes were made and when. These records
never contain event content and never contain tokens.
5.8 Retention and deletion
| Data | Retention |
|---|---|
| Shift ↔ event ID mapping | For as long as the user keeps the feature enabled · deleted immediately on "Disconnect" |
| Linked account email | Same |
The user's calFeed entry | Holds only the latest published version of each roster month · when a month's roster changes, that month's entry is overwritten wholesale; no edit history is kept |
calAck receipt | Only the most recent record · overwritten on every sync |
Pressing "Disconnect" in the app (1) deletes every event this app
created from the user's calendar, (2) deletes the mappings and settings from
PropertiesService, (3) deletes the installed trigger, and (4) revokes the script's
authorization — all from a single button press. (If a specific event cannot be deleted at that
moment — e.g. the calendar is temporarily unreachable — the user is informed, and the mapping for
that one event is retained so deletion can be completed on a later attempt rather than leaving an
orphaned event behind.)
5.9 Breach response
If we become aware of an incident affecting user data, we will (1) suspend the feature and revoke authorizations immediately, (2) notify affected users by email and via in-app announcement within 72 hours of becoming aware, and (3) notify Google through the designated channel if the incident involves data obtained from Google APIs.
5.10 Personnel with access
The system is built and maintained by a single developer (the contact in §9). There are no employees, no subcontractors, and no third parties with access. The developer's Google account that owns the project has 2-Step Verification enabled.
6. Third-party sharing
We share no Google API data with any third party. There are no analytics services, no trackers, no advertising, and no external CDN that receives user data. The only data processor is Google LLC, as the infrastructure provider (Apps Script, Calendar, Firebase).
7. User rights
Users have the right to access, correct, obtain a copy of, object to the processing of, and delete their data, under Thailand's Personal Data Protection Act B.E. 2562 (PDPA). All of it can be exercised immediately and without contacting us:
- Disconnect in-app — the "Disconnect" button on the "Calendar settings" screen deletes every event created, deletes all stored data, and removes the trigger.
- Delete events only, staying connected — the "Clear all shift events" button.
- Revoke from Google's side — at Google Account permissions, select "Siriraj Rx Shift Calendar" and remove access.
- Turn off automatic updates only — the "Automatic updates" toggle (removes the trigger; already synced events remain).
Deletion requests and other enquiries: the email in §9. We respond within 30 days.
8. Children
The system is used exclusively by licensed pharmacy professionals. It is not designed for or directed at anyone under 18, and we do not knowingly collect data from minors.
9. Changes to this policy, and contact
If we make material changes we will update the "Last updated" date above and notify users through an in-app announcement.
Data controller / contact: Norapol Uttho —
norapol.uttho@gmail.com
App website: https://sirxshift.com/