ข้ามไปยังเนื้อหา
Dee Wan 1.0 ยังอยู่ระหว่างพัฒนาและยังไม่เปิดตัว (pre-MVP) — ดู ขอบเขตผลิตภัณฑ์ 1.0

API อ่านข้อมูลสาธารณะ (v1)

public read API ให้บริการเนื้อหาที่เผยแพร่แล้วแก่ไซต์ของคุณ ทุก endpoint ด้านล่างระบุไซต์จาก header Host และจากสิ่งนั้นเท่านั้น ไม่มีพารามิเตอร์ระบุไซต์ ไม่มี header ระบุไซต์ และผู้เรียกไม่มีทางเลือกไซต์ได้เลย โทเคนผูกกับไซต์ที่ระบุ สิทธิ์แชร์แบบชัดเจนเพิ่มข้อมูลสาธารณะที่เผยแพร่ของต้นทางได้ แต่ไม่อนุญาตคำค้นอิสระหรือการแก้ไขไซต์ต้นทาง

API นี้อ่านได้อย่างเดียว หากต้องการสร้าง แก้ไข เผยแพร่ หรืออัปโหลดจากสคริปต์หรือเอเจนต์ AI ให้ใช้ API จัดการ CMS พร้อมข้อมูลรับรองสำหรับการจัดการ

สำหรับ API ที่ดีพลอยและใช้อาร์กิวเมนต์ Prisma ดู การอ่านข้อมูลจาก API แอป โดย route มาตรฐาน route ของคำขอที่บันทึก และความสามารถเต็มของ prisma-generator-express มีสัญญาต่างกัน API อ่าน /v1 นี้ใช้ภาษาคำขอที่มีขอบเขตของตนเอง

public API รันเป็น Worker ของตัวเอง มันให้บริการไซต์บน hostname ที่เก็บไว้เป็น domain ของไซต์นั้น: header Host ที่แปลงเป็นตัวพิมพ์เล็กและไม่มี port ต้องตรงกับ ค่านั้นทุกประการ ไม่มีการทำ alias www และไม่มีการ fallback ไปยังไซต์อื่น

ที่ การตั้งค่า → API → ต้นทางของเบราว์เซอร์ ผู้ดูแลโครงสร้างพื้นฐานสามารถอนุญาตให้อ่าน API นี้จากเบราว์เซอร์ได้โดยตรง ใส่ต้นทาง HTTP(S) หนึ่งรายการต่อบรรทัด โดยไม่มีพาธหรือ / ต่อท้าย เช่น https://www.example.com หรือ http://localhost:5173 รายการว่างไม่อนุญาตการอ่านข้ามต้นทาง ใช้ * เพื่ออนุญาตการอ่านข้อมูลสาธารณะจากทุกต้นทาง ไม่อนุญาตคุกกี้ของเบราว์เซอร์ เก็บโทเค็น API ส่วนตัวของไซต์ไว้บนเซิร์ฟเวอร์

ต้นทางที่อนุญาตจะได้รับส่วนหัว CORS ทั้งเมื่อสำเร็จและเมื่อเกิดข้อผิดพลาด คำขอ OPTIONS อนุญาตเฉพาะ GET, HEAD, OPTIONS และส่วนหัว Authorization, Content-Type, X-Site-Key, X-Site-Token, X-Dee-Wan-Preview-Challenge ต้นทางอื่นไม่ได้รับส่วนหัวอนุญาต ทุกคำขอยังระบุไซต์จากชื่อโฮสต์และตรวจสอบกฎการเผยแพร่กับโทเค็นตามเดิม การตั้งค่านี้ใช้เฉพาะ API อ่าน /v1 ที่ใช้งานอยู่ ไม่เปลี่ยน API ผู้ดูแล API การจัดการ หรือ API แอปที่สร้างขึ้น คำขอจากเซิร์ฟเวอร์และระหว่างบิลด์ไม่ต้องตั้งค่า CORS

Worker ไม่ได้จำกัดอัตรา (rate limit) เอง หากคุณต้องการ ให้ตั้งค่าบน Cloudflare zone ที่ให้บริการ hostname สาธารณะ

ซิงก์เนื้อหาที่เผยแพร่แล้ว อ่าน snapshot แบบแบ่งหน้าที่มีขอบเขต จากนั้นอ่านการเปลี่ยนแปลงของรายการและสื่อสาธารณะตามลำดับ พร้อม checkpoint ที่ลงลายเซ็น ใช้โทเคนไซต์ที่จำกัดสิทธิ์และกฎ CORS เดิม

พารามิเตอร์ จำเป็น ความหมาย
cursor ไม่ next_cursor หรือ checkpoint จากหน้าก่อนหน้าโดยไม่แก้ไข
limit ไม่ ขนาดหน้า 1–100 ค่าเริ่มต้น 50
model ไม่ จำกัดรายการด้วย slug ของโมเดลที่มีสิทธิ์อ่านหนึ่งโมเดล
locale ไม่ จำกัดรายการไว้ภาษาเดียว
สถานะ ความหมาย
200 snapshot หรือการเปลี่ยนแปลงตามลำดับ พร้อม cursor สำหรับหน้าถัดไปหรือ checkpoint
400 sync_query_invalid: โมเดลหรือคำค้นไม่ถูกต้อง หรือไม่มีสิทธิ์อ่านโมเดล
404 ไม่รู้จักโฮสต์หรือโทเคน API ของไซต์ไม่ถูกต้อง
409 reset_required: เริ่ม snapshot ใหม่
503 sync_dependency_unavailable: ลอง checkpoint เดิมอีกครั้ง
หน้าต่างเทอร์มินัล
GET /v1/_sync?limit=50
{ "data": [], "deleted": [], "changes": [], "phase": "entries", "next_cursor": "opaque", "checkpoint": null, "reset_reason": null }

host นี้ให้บริการไซต์อยู่หรือไม่ ตอบเฉพาะ host ที่ deployment นี้รู้จัก host ที่ไม่รู้จักจะได้ 404 ที่นี่ เหมือนกับทุก route อื่น ดังนั้นจึงใช้ route นี้ค้นหาว่ามีไซต์ใดอยู่บ้างไม่ได้

Status ความหมาย
200 host นี้ให้บริการไซต์ body มี slug ของไซต์
404 unknown_host: ไม่มีไซต์ที่ active ตอบบน host นี้
หน้าต่างเทอร์มินัล
curl https://example.com/health
{ "ok": true, "site": "acme" }

แสดงรายการ item ที่เผยแพร่แล้วของ model หนึ่ง เฉพาะ item ที่เผยแพร่แล้ว เรียงตามเวลาเผยแพร่แล้วตาม id เว้นแต่ sort ระบุฟิลด์ ฐานข้อมูลคัดเลือกตามภาษา ใช้ตัวกรอง และแบ่งหน้าก่อนส่งผลลัพธ์ ดังนั้นหน้าหนึ่งจะไม่สั้นกว่า limit เพราะมีบางอย่างถูกทิ้งไปภายหลัง หน้าที่สั้นกว่า limit พร้อม has_more: false คือจุดสิ้นสุดของรายการ

พารามิเตอร์ จำเป็น ความหมาย
locale ไม่ ภาษาที่จะให้บริการ เป็นรหัสที่ตรงกับ ^[A-Za-z0-9_-]{1,64}$ ค่าเริ่มต้นคือภาษาเริ่มต้นของไซต์ สิ่งที่เกิดขึ้นเมื่อ item ไม่มีเนื้อหาในภาษานั้นถูกกำหนดโดยนโยบาย locale ของไซต์ ไม่ใช่โดยพารามิเตอร์นี้ ดู Locale ด้านล่าง
origin ไม่ ละติจูด,ลองจิจูดสำหรับเรียงตามระยะทาง ต้องระบุเมื่อเรียงตามฟิลด์จุด และใช้กับการเรียงแบบอื่นไม่ได้
limit ไม่ ขนาดหน้า เป็นจำนวนเต็มตั้งแต่ 1 ถึง 100 ค่าเริ่มต้น 20 ค่าอื่นใด รวมถึงค่าที่เกิน 100 จะถูกปฏิเสธด้วย 400 public_filter_invalid ไม่มีการปรับค่าให้อยู่ในช่วง
cursor ไม่ เป็นค่าที่ผู้เรียกไม่ต้องตีความ (opaque) ให้นำมาจาก page.next_cursor และส่งกลับไปโดยไม่เปลี่ยนแปลง พร้อม query เดียวกันทุกประการกับที่ใช้ตอนออก cursor: path ของ model เดิม, locale, order หรือ sort, filter และค่าของมัน, fields และ expand เดิม เปลี่ยนได้เฉพาะ limit cursor ที่ส่งมากับ query อื่นจะถูกปฏิเสธด้วย 400 public_cursor_mismatch cursor ที่ decode ไม่ได้ หรือที่ออกก่อนที่ cursor จะเก็บ query ของตัวเอง จะถูกปฏิเสธด้วย 400 public_filter_invalid ให้เริ่มใหม่จากหน้าแรก รูปแบบข้อมูลภายในไม่ได้รับประกันว่าจะคงเดิมและอาจเปลี่ยนได้
order ไม่ asc, desc, manual — เรียงตามเวลาเผยแพร่ ค่าเริ่มต้นคือ desc (ใหม่สุดก่อน) ส่วน manual ขอลำดับที่กำหนดเองของคอลเลกชันเมื่อเปิดใช้ โดยใช้ลำดับเดียวกันทุกภาษา ห้ามใช้ order ร่วมกับ sort เมื่อเปลี่ยนลำดับที่กำหนดเองแล้ว cursor เดิมจะใช้ไม่ได้ ต้องเริ่มจากหน้าแรก
filter ไม่ filter[<field>]=<value> เก็บ item ที่ฟิลด์มีค่าเท่ากับค่านั้นใน locale ที่ให้บริการ ใส่ซ้ำเพื่อให้มีหลายทางเลือก: filter[kind]=guide&filter[kind]=news เก็บ item ที่ kind เป็นค่าใดค่าหนึ่ง ไม่เกิน 10 ค่าต่อฟิลด์ ฟิลด์ข้อความ slug และ select เปรียบเทียบข้อความ ฟิลด์ boolean รับ true หรือ false ฟิลด์ตัวเลขรับเลขทศนิยม ฟิลด์วันที่รับ YYYY-MM-DD และเปรียบเทียบตามวันในปฏิทิน ในฟิลด์ตัวเลขและวันที่ filter[<field>][gt] และ [lt] เป็นขอบแบบไม่รวมค่าขอบ ส่วน [gte] และ [lte] เป็นขอบแบบรวมค่าขอบ ฟิลด์หนึ่งรับขอบล่างได้หนึ่งตัว (gt หรือ gte) และขอบบนได้หนึ่งตัว (lt หรือ lte) ใช้ gt คู่กับ gte หรือ lt คู่กับ lte ไม่ได้ และใช้ขอบร่วมกับการเท่ากับไม่ได้ ช่วงที่ไม่มีค่าใดเข้าเงื่อนไขได้ เช่น [gt]=5&[lt]=5 ได้ public_filter_invalid ฟิลด์ต่างกันรวมกันแบบ AND ได้ไม่เกิน 4 ฟิลด์ต่อ request แต่ละค่ายาวไม่เกิน 255 ตัวอักษร ฟิลด์มากกว่านั้นได้ public_filter_too_many ค่ามากกว่านั้นได้ public_filter_too_many_values ฟิลด์ที่ชนิดกรองแบบนี้ไม่ได้ได้ public_filter_type_unsupported ฟิลด์ที่ไม่รู้จักหรือค่าที่มีรูปแบบผิดได้ public_filter_invalid request จะไม่ถูกตัดให้แคบลง ค่าข้อความที่ยาวเกิน 255 ตัวอักษรจะไม่ถูกใช้จับคู่หรือเรียงลำดับเลย
sort ไม่ ฟิลด์ชนิด number, date, text, select หรือ point หนึ่งฟิลด์: sort=<field> เรียงจากน้อยไปมาก sort=-<field> เรียงจากมากไปน้อย แล้วตาม id สำหรับจุด sort=<field> เรียงใกล้สุดก่อน และ sort=-<field> เรียงไกลสุดก่อน ทั้งสองแบบต้องระบุ origin=<latitude>,<longitude> รายการที่ไม่มีค่าชนิดนั้นอยู่ท้ายสุดทั้งสองทิศทาง ใช้ร่วมกับ order ไม่ได้
fields ไม่ คีย์ฟิลด์ระดับบนสุดที่จะส่งคืน คั่นด้วยจุลภาค ไม่เกิน 32 คีย์ ไม่ซ้ำ id, slug และ locale ส่งคืนเสมอ
relation ไม่ กรองตามฟิลด์ relation ในรูปแบบ relation=<field>:<target item id> เก็บ item ที่ฟิลด์ relation ให้บริการเป้าหมายนั้นใน relations เป้าหมายที่ request นี้อ่านไม่ได้จะไม่ตรงเลย <field> ต้องเป็นฟิลด์ relation ของ model นี้ที่รับ model เดียว ฟิลด์ที่รับหลาย model จะถูกปฏิเสธด้วย public_filter_type_unsupported ใส่ซ้ำได้ การใส่ซ้ำรวมกันแบบ AND
tag ไม่ กรองตามฟิลด์ relation โดยจับคู่ด้วย slug ของเป้าหมาย: tag=<field>:<target slug> ตรงได้เฉพาะเป้าหมายที่ item ให้บริการใน relations โดยใช้ที่อยู่ที่เผยแพร่ซึ่งให้บริการอยู่ตรงนั้น: ในภาษาที่ item ให้บริการ จากนั้นตามลำดับภาษาสำรองของไซต์ ภายใต้ strict เป้าหมายที่แปลแล้วซึ่งไม่มีเวอร์ชันที่เผยแพร่ในภาษานั้นจะไม่ตรง เป้าหมายที่ไม่ได้แปลตรงได้ในทุกภาษา ไม่มีประเภทฟิลด์ tag แยกต่างหาก <field> ต้องเป็นฟิลด์ relation ของ model นี้ที่รับ model เดียว ฟิลด์ที่รับหลาย model จะถูกปฏิเสธด้วย public_filter_type_unsupported ใส่ซ้ำได้ การใส่ซ้ำรวมกันแบบ AND คู่ relation และ tag รวมกันได้ไม่เกิน 16 คู่ต่อ request หากมากกว่านั้นจะถูกปฏิเสธด้วย 400 public_filter_too_many ซึ่ง body มี max request จะไม่ถูกตัดให้แคบลงแบบเงียบ ๆ เพื่อให้พอดี
expand ไม่ สำหรับการอ่านทั่วไปและลิงก์ตัวอย่างเก่าที่ไม่มีสิทธิ์อ่านฉบับร่างที่เกี่ยวข้อง: ฟิลด์ relation ของ model นี้คั่นด้วยจุลภาค ไม่เกิน 3 ฟิลด์ ไม่ซ้ำ จากนั้นแต่ละ item จะมี expanded.<field>: { data, page } โดย data มีเป้าหมายเป็น item ของ model เป้าหมายของฟิลด์ ตามลำดับในฟิลด์ ให้บริการเฉพาะเป้าหมายที่เผยแพร่แล้วของไซต์นี้ที่ request นี้อ่านได้ ใน locale ที่ขอตามนโยบาย locale ของไซต์ เหมือนการอ่าน model เป้าหมาย model เป้าหมายแบบส่วนตัวให้บริการเฉพาะในการอ่าน model ส่วนตัวที่ token ระบุ model นั้น ใน response มีเป้าหมายไม่เกิน 25 รายการต่อฟิลด์ ถ้ามีมากกว่านั้น page.next_cursor ใช้อ่านส่วนที่เหลือจาก /v1/_related/:model ขยายได้ระดับเดียว: item ที่ขยายแล้วมี relations แต่ไม่มี expanded path ที่มีจุดจะถูกปฏิเสธด้วย public_expand_too_deep ฟิลด์มากกว่านั้นด้วย public_expand_too_many และฟิลด์ที่ไม่ใช่ฟิลด์ relation ของ model นี้ หรือที่รับหลาย model ด้วย public_expand_invalid ลิงก์ตัวอย่างใหม่ขยายฉบับร่างที่บันทึกแล้วและมีสิทธิ์อ่านโดยอัตโนมัติ ดู ตัวอย่างฉบับร่าง
Status ความหมาย
200 หน้าหนึ่งของ item ใน data พร้อม page.limit, page.has_more และ page.next_cursor อยู่ข้าง ๆ next_cursor เป็น null ในหน้าสุดท้าย
400 public_filter_invalid: พารามิเตอร์ที่ไม่รู้จัก, ค่าว่าง, พารามิเตอร์ที่ใส่ซ้ำซึ่งไม่ใช่ relation, tag หรือ filter[<field>] หรือค่าที่มีรูปแบบผิด reason นี้ยังครอบคลุมฟิลด์ของ relation หรือ tag ที่ไม่ใช่ฟิลด์ relation ของ model นี้ หรือ cursor ที่ decode ไม่ได้ด้วย public_filter_too_many, public_filter_too_many_values: เกินเพดานที่ max ระบุ public_filter_type_unsupported: filter บนฟิลด์ที่ชนิดรองรับไม่ได้ public_cursor_mismatch: cursor จาก query อื่น public_expand_invalid, public_expand_too_many, public_expand_too_deep: ดู expand
404 ไม่มีไซต์บน host นี้ (unknown_host) หรือไม่มี model ที่มี slug นี้ หรือเป็น model ส่วนตัวและไม่มี site API token ที่ถูกต้อง สองกรณีหลังให้ body เดียวกันโดยไม่มี reason ผู้เรียกจึงแยก model ส่วนตัวออกจาก model ที่ไม่มีอยู่ไม่ได้
500 site_locale_policy_invalid: ไซต์ไม่มีนโยบาย locale ที่ใช้ได้เก็บไว้ ผู้ดูแลต้องตั้งค่าที่ การตั้งค่า → ภาษา ความล้มเหลวที่ไม่คาดคิดจะคืน Internal Server Error แบบข้อความธรรมดา
หน้าต่างเทอร์มินัล
curl 'https://example.com/v1/article?locale=th&limit=10&order=desc'
{ "contracts": { "page": { "served": 5 } },
"data": [
{ "id": "ci_…", "slug": "hello", "model": "article", "locale": "th", "version": 3,
"published_at": "2026-09-01T08:00:00.000Z",
"fields": { "title": "สวัสดี" }, "relations": {}, "media": {} }
],
"page": { "limit": 10, "has_more": true, "next_cursor": "…" } }

อ่าน item ที่เผยแพร่แล้วหนึ่งรายการ หนึ่ง item ตาม slug ของเวอร์ชันที่เผยแพร่ แต่ละภาษามี slug ของตัวเอง: /v1/post/bonjour?locale=fr ส่งภาษาฝรั่งเศสของรายการที่มี slug ภาษาอังกฤษเป็น hello และแต่ละภาษาตอบเฉพาะที่ slug ของตัวเอง slug ไม่ซ้ำกันภายใน model และภาษาหนึ่งบนไซต์หนึ่ง แต่ไม่ใช่ข้ามไซต์ การเปลี่ยนชื่อ draft ไม่ย้ายที่อยู่ที่เผยแพร่แล้วจนกว่าการเปลี่ยนชื่อจะถูกเผยแพร่ item มี alternates: [{ locale, slug }] คือทุกภาษาที่รายการเผยแพร่อยู่พร้อม slug ของภาษานั้น รวมภาษาที่ส่งออกมาด้วย

พารามิเตอร์ จำเป็น ความหมาย
locale ไม่ ภาษาที่จะให้บริการ เป็นรหัสที่ตรงกับ ^[A-Za-z0-9_-]{1,64}$ ค่าเริ่มต้นคือภาษาเริ่มต้นของไซต์ สิ่งที่เกิดขึ้นเมื่อ item ไม่มีเนื้อหาในภาษานั้นถูกกำหนดโดยนโยบาย locale ของไซต์ ไม่ใช่โดยพารามิเตอร์นี้ ดู Locale ด้านล่าง
preview ไม่ preview token (dw_prev_…) ที่ CMS ออกให้เมื่อผู้แก้ไขกด Preview ให้บริการ DRAFT ปัจจุบันของ รายการหลักหนึ่งรายการ โดยจับคู่กับ slug ของ draft เป็นเวลา 30 นาที preview ที่ให้บริการแล้วไม่ถูก cache เลย มันไม่ทำให้ model ส่วนตัวอ่านได้ token ที่ตรวจสอบไม่ผ่านจะตอบ 404 ไม่ใช่ 403 ลิงก์ตัวอย่างใหม่ยังอ่านฉบับร่างที่บันทึกแล้วของรายการที่เกี่ยวข้องและมีสิทธิ์อ่าน พร้อมตรวจสิทธิ์ปัจจุบันของผู้สร้างอีกครั้ง สิทธิ์ฟิลด์และภาษาของรายการหลักยังจำกัดตามสิทธิ์ที่บันทึกไว้เมื่อสร้างลิงก์ ดู ตัวอย่างฉบับร่าง
expand ไม่ สำหรับการอ่านทั่วไปและลิงก์ตัวอย่างเก่าที่ไม่มีสิทธิ์อ่านฉบับร่างที่เกี่ยวข้อง: ฟิลด์ relation ของ model นี้คั่นด้วยจุลภาค ไม่เกิน 3 ฟิลด์ ไม่ซ้ำ จากนั้นแต่ละ item จะมี expanded.<field>: { data, page } โดย data มีเป้าหมายเป็น item ของ model เป้าหมายของฟิลด์ ตามลำดับในฟิลด์ ให้บริการเฉพาะเป้าหมายที่เผยแพร่แล้วของไซต์นี้ที่ request นี้อ่านได้ ใน locale ที่ขอตามนโยบาย locale ของไซต์ เหมือนการอ่าน model เป้าหมาย model เป้าหมายแบบส่วนตัวให้บริการเฉพาะในการอ่าน model ส่วนตัวที่ token ระบุ model นั้น ใน response มีเป้าหมายไม่เกิน 25 รายการต่อฟิลด์ ถ้ามีมากกว่านั้น page.next_cursor ใช้อ่านส่วนที่เหลือจาก /v1/_related/:model ขยายได้ระดับเดียว: item ที่ขยายแล้วมี relations แต่ไม่มี expanded path ที่มีจุดจะถูกปฏิเสธด้วย public_expand_too_deep ฟิลด์มากกว่านั้นด้วย public_expand_too_many และฟิลด์ที่ไม่ใช่ฟิลด์ relation ของ model นี้ หรือที่รับหลาย model ด้วย public_expand_invalid ลิงก์ตัวอย่างใหม่ขยายฉบับร่างที่บันทึกแล้วและมีสิทธิ์อ่านโดยอัตโนมัติ ดู ตัวอย่างฉบับร่าง
Status ความหมาย
200 รายการที่แสดงค่าของภาษาเดียว
400 public_filter_invalid: พารามิเตอร์อื่นนอกจาก locale, preview และ expand, พารามิเตอร์ที่ใส่ซ้ำหรือว่าง หรือ locale ที่มีรูปแบบผิด public_expand_invalid, public_expand_too_many, public_expand_too_deep: ดู expand
404 unknown_host: ไม่มีไซต์บน host นี้ locale_unavailable: item ถูกเผยแพร่แล้วแต่ไม่มีเนื้อหาที่นโยบาย locale ของไซต์จะให้บริการ ambiguous_slug: item ที่เผยแพร่แล้วสองรายการอ้าง slug นี้ จึงไม่มีรายการใดถูกให้บริการ ไม่มี reason: ไม่มี model นี้, ไม่มี item ที่เผยแพร่แล้วซึ่งมี slug นี้ หรือ preview token ที่ตรวจสอบไม่ผ่าน model ส่วนตัวที่ไม่มี token ที่ถูกต้องก็ได้ response นี้เช่นกัน
422 เฉพาะ preview ของหน้า: media ของหน้า draft แสดงผลไม่ได้ reason เป็นหนึ่งใน unreadable_page, page_media_unavailable, media_presentation_unreadable, media_presentation_revision_unavailable, media_presentation_mismatch, media_size_missing และ error มีข้อความที่อ่านได้
500 site_locale_policy_invalid: ไซต์ไม่มีนโยบาย locale ที่ใช้ได้เก็บไว้ ผู้ดูแลต้องตั้งค่าที่ การตั้งค่า → ภาษา ความล้มเหลวที่ไม่คาดคิดจะคืน Internal Server Error แบบข้อความธรรมดา
หน้าต่างเทอร์มินัล
curl 'https://example.com/v1/article/hello?locale=th'
{ "contracts": { "page": { "served": 5 } }, "data": { "id": "ci_…", "slug": "hello", "model": "article", "locale": "th", "version": 3,
"published_at": "2026-09-01T08:00:00.000Z",
"fields": { "title": "สวัสดี" }, "relations": {}, "media": {} } }

อ่านส่วนที่เหลือของฟิลด์ relation ที่ขยาย เป้าหมายของฟิลด์ relation ที่ขยายหนึ่งฟิลด์หลังหน้าแรก :model คือ model ของ item ที่มีฟิลด์นั้น cursor ระบุ item นั้น เวอร์ชันที่เผยแพร่ ฟิลด์ และ locale สิ่งที่ให้บริการตัดสินด้วยกฎเดียวกับ expand cursor ใช้ได้เฉพาะบนไซต์นี้ สำหรับ model นี้ และสำหรับ token ที่มีชุด model เดียวกับ request ที่ออก cursor model ส่วนตัวต้องใช้ token ที่นี่ด้วย

พารามิเตอร์ จำเป็น ความหมาย
cursor ใช่ expanded.<field>.page.next_cursor ของ item หรือ page.next_cursor ของคำตอบก่อนหน้าที่นี่ ส่งกลับไปโดยไม่เปลี่ยนแปลง cursor จากไซต์อื่น model อื่น หรือชุด model ของ token อื่น จะถูกปฏิเสธด้วย public_cursor_mismatch หลังจาก item ถูกเผยแพร่อีกครั้ง จะถูกปฏิเสธด้วย public_cursor_stale ให้อ่าน item ใหม่
limit ไม่ ขนาดหน้า เป็นจำนวนเต็มตั้งแต่ 1 ถึง 100 ค่าเริ่มต้น 25
Status ความหมาย
200 หน้าหนึ่งของ item ที่เกี่ยวข้องใน data พร้อม page.limit, page.has_more และ page.next_cursor
400 public_filter_invalid: ไม่มี cursor, พารามิเตอร์อื่นนอกจาก cursor และ limit, พารามิเตอร์ที่ใส่ซ้ำหรือว่าง, limit ที่ไม่ถูกต้อง หรือ cursor ที่ decode ไม่ได้ public_cursor_mismatch: cursor จากไซต์อื่น model อื่น หรือชุด model ของ token อื่น public_cursor_stale: item ถูกเผยแพร่อีกครั้ง หรือฟิลด์ไม่ใช่ฟิลด์ relation ที่ขยายได้อีกต่อไป
404 unknown_host: ไม่มีไซต์บน host นี้ ไม่มี reason: ไม่มี model นี้ เป็น model ส่วนตัวที่ไม่มี token ที่ถูกต้อง หรือ item ไม่ได้เผยแพร่ใน locale นั้นแล้ว
500 site_locale_policy_invalid: ไซต์ไม่มีนโยบาย locale ที่ใช้ได้เก็บไว้ ผู้ดูแลต้องตั้งค่าที่ การตั้งค่า → ภาษา ความล้มเหลวที่ไม่คาดคิดจะคืน Internal Server Error แบบข้อความธรรมดา
หน้าต่างเทอร์มินัล
curl 'https://example.com/v1/_related/article?cursor=…&limit=25'
{ "contracts": { "page": { "served": 5 } },
"data": [ { "id": "ci_…", "slug": "ana", "model": "author", "locale": "en", … } ],
"page": { "limit": 25, "has_more": false, "next_cursor": null } }

ดาวน์โหลด ไคลเอนต์ TypeScript ที่กำหนดชนิดข้อมูลไว้ชัดเจน client ที่ไม่มี dependency สำหรับเว็บไซต์ของโฮสต์นี้ พร้อม schema identity ที่ใช้สร้างมัน ต้องใช้ site API token แม้ทุกโมเดลจะเป็นสาธารณะ client ครอบคลุมโมเดลสาธารณะทั้งหมดและโมเดลส่วนตัวที่ token อ่านได้ schema_identity คือ digest แบบ SHA-256 ของ contract ได้แก่ ภาษา นโยบายภาษา และการมองเห็นกับชนิดฟิลด์ของแต่ละโมเดล ป้ายชื่อและลำดับฟิลด์ไม่ทำให้มันเปลี่ยน source ขึ้นต้นด้วยคอมเมนต์ที่บันทึกค่าเดียวกัน คำสั่งจาก การตั้งค่า → API อ่าน route นี้ คำตอบไม่ถูกแคชเลย

สถานะ ความหมาย
200 data มี contract_version (1), schema_identity, site, url, models, contract และ source
400 public_filter_invalid: คำขอมี query string
404 ไม่มีเว็บไซต์บนโฮสต์นี้ (unknown_host) หรือไม่มี site API token ที่ใช้ได้สำหรับเว็บไซต์นี้ token ที่ไม่มี หมดอายุ ถูกเพิกถอน หรือเป็นของเว็บไซต์อื่น ได้ body เดียวกันโดยไม่มี reason
หน้าต่างเทอร์มินัล
curl -H "Authorization: Bearer $DEE_WAN_SITE_TOKEN" https://example.com/v1/_client
{ "data": { "contract_version": 1, "schema_identity": "sha256:9f2c…", "site": "acme",
"url": "https://example.com", "models": ["article"],
"contract": { "site": "acme", "default_locale": "en", "locales": ["en", "th"],
"locale_policy": "strict", "models": { "article": { "visibility": "public",
"fields": { "title": { "type": "text", "required": true, "multiple": false,
"options": null } } } } },
"source": "/* @dee-wan/public-client …" } }

อ่านรายการ redirect ที่ยอมรับแล้ว กฎ redirect ใน เมนูและ SEO เวอร์ชันที่ยอมรับแล้ว (ACCEPTED) ทั้งที่สร้างเองและที่การเผยแพร่เพิ่มให้เมื่อย้ายที่อยู่ที่ใช้งานจริง renderer อ่าน route นี้เมื่อ path ไม่ตรงกับหน้าใด match: "exact" ระบุ path เดียว match: "pattern" ผูกส่วน {name} และ {name*} ตัวสุดท้าย ซึ่ง to นำไปใช้ซ้ำได้ กฎแบบตรงตัวมาก่อนรูปแบบ และรูปแบบที่มีส่วนคงที่มากที่สุดชนะรูปแบบอื่น status คือ 301, 302, 307 หรือ 308 พร้อม to (path บนไซต์นี้หรือที่อยู่ https) หรือ 410 (ลบแล้ว) หรือ 451 (ไม่พร้อมให้บริการด้วยเหตุผลทางกฎหมาย) พร้อม to: null ร่างของ เมนูและ SEO ที่ยังไม่ยอมรับจะไม่ปรากฏที่นี่ คำตอบเหมือนกันทั้งมีและไม่มี token ถูก cache และถูกล้างเมื่อยอมรับ เมนูและ SEO

Status ความหมาย
200 data.redirects: รายการ { from, match, status, to } อาจว่างได้
400 public_filter_invalid: request มี query string
404 unknown_host: ไม่มีไซต์บน host นี้
500 site_chrome_unreadable: เมนูและ SEO เวอร์ชันที่ยอมรับแล้วไม่ตรงกับ digest ที่บันทึกไว้อีกต่อไป ผู้ดูแลต้องยอมรับเวอร์ชันใหม่อีกครั้ง
หน้าต่างเทอร์มินัล
curl https://example.com/v1/_redirects
{ "data": { "redirects": [
{ "from": "/tours-old", "match": "exact", "status": 301, "to": "/tours" },
{ "from": "/news/{rest*}", "match": "pattern", "status": 308, "to": "/blog/{rest}" },
{ "from": "/old-offer", "match": "exact", "status": 410, "to": null } ] } }

อ่านเมนูและพื้นที่ของภาษาเดียว เมนู พื้นที่ของเทมเพลต และข้อความท้ายหน้า (footer note) ของ เมนูและ SEO เวอร์ชันที่ยอมรับแล้ว (ACCEPTED) ที่ resolve สำหรับภาษาเดียว รายการเมนูที่ชี้ไปยังหน้า โพสต์ รายการบล็อก หรือหน้ารวม (archive) จะมีที่อยู่ที่เผยแพร่อยู่ในวันนี้ของรายการนั้นในภาษานี้ (กฎ locale ของไซต์ตัดสิน fallback) รายการที่หน้ายังไม่เผยแพร่หรือ model เป็นแบบส่วนตัวจะถูกตัดออก และหัวข้อที่ไม่เหลือรายการข้างใต้ก็ถูกตัดออกด้วย แต่ละภาษามีรายการเมนูของตัวเอง ภาษาที่ไม่มีเวอร์ชันของตัวเองจะทำตามกฎ strict หรือ fallback ของไซต์ placements บอกว่าส่วนหัวและส่วนท้ายแสดงเมนูใดในภาษานี้ regions มีทุกพื้นที่ที่เทมเพลตของไซต์ประกาศไว้และมีสิ่งที่แสดงในภาษานี้ พร้อมรายการตามลำดับ: ส่วนที่เผยแพร่แล้วของส่วนที่ใช้ร่วมกัน เมนู หน้าค้นหา โพสต์บล็อกล่าสุด หมวดหมู่ แท็ก หรือ “โพสต์ตามเดือน” ส่วนที่ใช้ร่วมกันจะมีรายการของทุกส่วน “รายการล่าสุด” ที่อยู่ในนั้น อ่านแบบเดียวกับ GET /v1/:model: เฉพาะที่เผยแพร่แล้ว ในภาษานี้หรือภาษา fallback ไม่เกินจำนวนที่ส่วนนั้นกำหนด “โพสต์ตามเดือน” ลิงก์ไปยังโพสต์ใหม่ล่าสุดไม่เกิน 30 รายการ จัดกลุ่มไม่เกิน 12 เดือน เดือนไม่มีที่อยู่ของตัวเอง managed renderer อ่าน route นี้ทุกหน้า การเปลี่ยนเมนูและพื้นที่ รวมถึงหน้าที่ย้าย จึงไม่ต้องมี release ใหม่ คำตอบเหมือนกันทั้งมีและไม่มี token ถูก cache และถูกล้างเมื่อยอมรับ เมนูและ SEO หรือเมื่อ model หรือส่วนที่ใช้ร่วมกันที่ถูกอ้างอิงเผยแพร่

คำตอบมี robots: { allow_indexing, disallow } ซึ่งเป็นนโยบายการทำดัชนีที่ยอมรับแล้วของเว็บไซต์ด้วย หน้าแบบจัดการอ่านนโยบายจาก endpoint นี้และเก็บคำตอบที่สำเร็จไว้ไม่เกิน 30 วินาที หากอ่านนโยบายไม่ได้ หน้าจะมี noindex เว็บไซต์ที่สร้างขึ้นจะตรึงนโยบายไว้ตอนสร้าง จึงต้องสร้างและปรับใช้ใหม่เพื่อเปลี่ยนนโยบาย เมื่อปิดการทำดัชนี หน้าสาธารณะมี noindex แต่ยังอนุญาตให้โปรแกรมค้นหาเข้าถึงเพื่ออ่านแท็กนี้ พาธที่บันทึกไว้ในรายการห้ามเข้าถึงจะมีผลเมื่อเปิดการทำดัชนี

home คือข้อมูลหน้าแรกที่เผยแพร่แล้ว { model, slug, locale, path } หรือ null ส่วน branding เป็นข้อมูลเสริม มี title, tagline, logo_media_id, icon_media_id และรูป logo/icon ที่บันทึกไว้เมื่อมี โดยรูปมีฟิลด์ url, alt, width, height

พารามิเตอร์ จำเป็น ความหมาย
locale ไม่ ภาษาหนึ่งของไซต์นี้ ถ้าไม่ส่งจะใช้ภาษาเริ่มต้น
Status ความหมาย
200 data: { locale, default_locale, home, branding?, robots: { allow_indexing, disallow }, menus: { [key]: { lang, items: [{ id, label, href, children: [menu-node], new_tab? }] } }, placements: { header, footer }, footer_note: { lang, text } | null, regions: { [key]: { lang, items } } } lang คือภาษาที่ให้บริการจริง: เวอร์ชันของเมนูหรือพื้นที่เองสำหรับ locale หรือหากไม่มี ภายใต้กฎ fallback ของไซต์ จะเป็นเวอร์ชันแรกตามลำดับของมัน ไซต์แบบ strict ให้บริการเฉพาะ locale เมนูที่ไม่มีเวอร์ชันในภาษานั้นจะไม่ปรากฏ หัวข้อมี href: null รายการในพื้นที่คือ { kind: "shared_section", entry, lang, document, collections? } { kind: "menu", menu, name, lang, items } { kind: "search", href } { kind: "recent_posts" | "categories" | "tags", links: [{ label, href }] } หรือ { kind: "monthly_archive", months: [{ month: "YYYY-MM", links }] } (“โพสต์ตามเดือน”) collections จับคู่ id ของแต่ละส่วน “รายการล่าสุด” กับ { ok: true, entries } หรือ { ok: false, code } และจะไม่มีถ้าส่วนที่ใช้ร่วมกันไม่มีส่วนแบบนี้
400 public_chrome_invalid: locale ไม่ใช่ภาษาของไซต์นี้ public_filter_invalid: มีพารามิเตอร์ query อื่น หรือส่ง locale สองครั้ง
404 unknown_host: ไม่มีไซต์บน host นี้
500 site_chrome_unreadable: เมนูและ SEO เวอร์ชันที่ยอมรับแล้วไม่ตรงกับ digest ที่บันทึกไว้อีกต่อไป ผู้ดูแลต้องยอมรับเวอร์ชันใหม่อีกครั้ง
หน้าต่างเทอร์มินัล
curl https://example.com/v1/_chrome?locale=th
{ "data": { "locale": "th", "default_locale": "en", "home": null, "robots": { "allow_indexing": true, "disallow": [] }, "menus": { "main": { "lang": "th", "items": [
{ "id": "mi_about", "label": "เกี่ยวกับ", "href": "/th/about", "children": [] },
{ "id": "mi_help", "label": "Help", "href": null, "children": [
{ "id": "mi_call", "label": "Call", "href": "tel:+6621234567", "children": [] } ] } ] } },
"placements": { "header": "main", "footer": null }, "footer_note": null,
"regions": { "page_end": { "lang": "th", "items": [ { "kind": "search", "href": "/th/search" } ] } } } }

เมนูซ้อนได้ไม่เกินห้าระดับ มีรายการระดับบนสุดได้ 24 รายการ รายการย่อยต่อหนึ่งรายการหลักได้ 12 รายการ และรวมทั้งหมดไม่เกิน 60 รายการ แต่ละโหนดมีรูปแบบ { id, label, href, children, new_tab? } ค่า new_tab: true เปิดลิงก์ในแท็บใหม่ หากไม่มีค่านี้จะเปิดในแท็บเดิม renderer ใส่ target="_blank" และ rel="noopener noreferrer"

default_locale ระบุภาษาเริ่มต้นของเว็บไซต์ home เป็น { model, slug, locale, path } ของหน้าแรกที่ตั้งค่าและเผยแพร่แล้วในภาษาที่ขอ หรือภาษาสำรองที่นโยบายอนุญาต หากไม่มีจะเป็น null โดยอ้างอิงเส้นทาง / ที่กำหนดไว้ในเมนูและ SEO เท่านั้น ไม่ส่งเป้าหมายที่ยังเป็นฉบับร่าง เป็นข้อมูลส่วนตัว กำกวม หรือไม่พร้อมใช้งาน ตัวเรนเดอร์แบบจัดการจะใช้ข้อมูลนี้กับแผนที่เส้นทางที่ตรึงไว้ ไม่สร้างที่อยู่ด้วยการเติมรหัสภาษาเอง

อ่าน origin ของการฝัง origin https ที่ผู้ดูแลเพิ่มไว้ใน การตั้งค่า → การเชื่อมต่อ นอกเหนือจากตัวเล่น YouTube และ Vimeo ที่มีในตัว renderer จะแสดง frame ของการฝังใน rich text จาก origin เหล่านี้เฉพาะขณะที่ยังอยู่ในรายการนี้ มิฉะนั้นจะวาดลิงก์ธรรมดาไปยังที่อยู่เดียวกัน id แต่ละตัวคือ host ของ origin และบล็อกการฝังระบุมันใน data-provider renderer ของ Dee Wan อ่านคำตอบนี้เฉพาะสำหรับ document ที่มีการฝังแบบนี้ คำตอบเหมือนกันทั้งมีและไม่มี token ถูก cache และถูกล้างเมื่อบันทึกรายการ

Status ความหมาย
200 data.providers: รายการ { id, origin } อาจว่างได้
400 public_filter_invalid: request มี query string
404 unknown_host: ไม่มีไซต์บน host นี้
หน้าต่างเทอร์มินัล
curl https://example.com/v1/_embeds
{ "data": { "providers": [ { "id": "maps.example.com", "origin": "https://maps.example.com" } ] } }

อ่านนิยามบล็อก rich text ของปลั๊กอิน ชนิดบล็อก rich text ที่ปลั๊กอินที่ใช้งานอยู่ของไซต์ประกาศไว้: ฟิลด์ ป้ายชื่อ และ render tree ที่สร้างจากรายการ element ที่กำหนดตายตัว ไม่ใช่ markup บล็อกที่เก็บไว้ระบุ type และ version ของตนใน data-type และ data-version บน <figure data-block="plugin"> renderer วาดบล็อกจาก tree render ในนิยามของบล็อกนั้น โดย escape ทุกค่า และไม่วาดอะไรเลยสำหรับบล็อกที่ชนิดไม่อยู่ในรายการ นิยามไม่มี render หรือเวอร์ชันใหม่กว่าเวอร์ชันในรายการ renderer ของ Dee Wan อ่านคำตอบนี้เฉพาะสำหรับ document ที่มีบล็อกของปลั๊กอิน คำตอบเหมือนกันทั้งมีและไม่มี token ถูก cache และถูกล้างเมื่อติดตั้ง อัปเกรด ปิด เปิด หรือลบปลั๊กอิน

Status ความหมาย
200 data.blocks: รายการนิยาม อาจว่างได้
400 public_filter_invalid: request มี query string
404 unknown_host: ไม่มีไซต์บน host นี้
หน้าต่างเทอร์มินัล
curl https://example.com/v1/_rich-blocks
{ "data": { "blocks": [ { "type": "com.example.notes/callout", "plugin": "com.example.notes", "name": "callout", "version": 1, "label": "Callout", "fields": [ { "key": "title", "control": "text", "label": "Title" } ], "render": { "nodes": [ { "element": "strong", "children": [ { "field": "title" } ] } ] } } ] } }

อ่านนโยบาย robots และส่วนต่าง ๆ ของแผนผังเว็บไซต์ สิ่งที่ /robots.txt และ /sitemap.xml บอกสำหรับไซต์นี้ robots คือนโยบายของ เมนูและ SEO เวอร์ชันที่ยอมรับแล้ว sitemap.chunks ระบุทุกส่วนของที่อยู่ที่ใช้งานจริงแยกตามโมเดล ส่วนละไม่เกิน 5,000 ที่อยู่ renderer แสดงแต่ละส่วนที่ /sitemap-<model>-<number>.xml ว่างเมื่อปิดแผนผังเว็บไซต์ นับเฉพาะโมเดลสาธารณะและเวอร์ชันที่ใช้งานจริง รายการที่ตั้ง seo.robots: "noindex" จะไม่อยู่ในรายการ ถูก cache และถูกล้างเมื่อยอมรับ เมนูและ SEO หรือเมื่อเผยแพร่ในโมเดลที่อยู่ในรายการ

Status ความหมาย
200 data.robots: { allow_indexing, disallow }; data.sitemap: { enabled, change_frequency, chunks: [{ model, number, lastmod }] }
400 public_filter_invalid: request มี query string
404 unknown_host: ไม่มีไซต์บน host นี้
หน้าต่างเทอร์มินัล
curl https://example.com/v1/_sitemap
{ "data": { "robots": { "allow_indexing": true, "disallow": [] },
"sitemap": { "enabled": true, "change_frequency": "weekly",
"chunks": [ { "model": "page", "number": 1, "lastmod": "2026-10-01T09:00:00.000Z" } ] } } }

อ่านแผนผังเว็บไซต์หนึ่งส่วน ที่อยู่ที่ใช้งานจริงของส่วนที่ /v1/_sitemap ระบุ สมาชิกแต่ละภาษาอยู่ที่ที่อยู่ของตัวเอง พร้อมเวลาเผยแพร่ล่าสุดและรูปภาพไม่เกิน 20 รูป (รูปสำหรับแชร์ แล้วตามด้วยรูปที่เวอร์ชันที่ใช้งานจริงแสดง) model และ number คือส่วนที่ /v1/_sitemap แสดงไว้ ที่อยู่รายการของบล็อกมาก่อนในส่วน post

แผนผังจะแสดงเฉพาะที่อยู่ที่กำหนดตัวเองเป็น canonical เท่านั้น ที่อยู่ canonical แบบเต็มจะเทียบกับโดเมนจากการผูกเว็บไซต์ที่ใช้งานอยู่ หากไม่มีโดเมนนี้ ระบบจะไม่รวมที่อยู่นั้น URL ของรูปภาพใช้สิทธิ์เข้าถึงฟิลด์และไฟล์ต้นฉบับของแต่ละภาษาแบบเดียวกับเนื้อหาสาธารณะ รวมถึงฟิลด์ส่วนตัวในคอมโพเนนต์ที่ซ้อนอยู่

Status ความหมาย
200 data.entries: รายการ { path, lastmod, images }
400 public_filter_invalid: request มี query string
404 sitemap_chunk_missing: ไม่มีส่วนนี้ หรือปิดแผนผังเว็บไซต์อยู่
หน้าต่างเทอร์มินัล
curl https://example.com/v1/_sitemap/page/1
{ "data": { "model": "page", "number": 1, "entries": [
{ "path": "/tours", "lastmod": "2026-10-01T09:00:00.000Z", "images": ["https://media.example.com/boat.jpg"] } ] } }

ค้นหาในไซต์ที่เผยแพร่อยู่ การค้นหาของไซต์ในสิ่งที่เผยแพร่อยู่ตอนนี้: หน้าที่เผยแพร่ โพสต์บล็อกบนไซต์บล็อก และ model สาธารณะอื่นที่การตั้งค่าการค้นหาของไซต์ระบุ ร่าง ภาษาที่เลิกเผยแพร่ item ในถังขยะ section ที่ใช้ร่วมกัน (shared section) และ model ส่วนตัวจะไม่ปรากฏ ส่วนการเผยแพร่ การถอน หรือการย้ายที่อยู่จะเห็นผลทันที แต่ละผลลัพธ์ให้บริการในภาษาที่ขอ หรือภายใต้นโยบาย fallback ในภาษาแรกตามลำดับภาษาสำรองที่มีเวอร์ชันเผยแพร่อยู่ slug คือที่อยู่ที่เผยแพร่ในภาษานั้น ทุกคำใน q ต้องตรงตามการจับคู่ของ model จากการตั้งค่าการค้นหา: word (ค่าเริ่มต้น) ทั้งคำ, english_stem รูปคำภาษาอังกฤษ (tours พบ tour) ในผลลัพธ์ภาษาอังกฤษ (en, en-* หรือ en_*) และทั้งคำในผลลัพธ์ภาษาอื่น, substring ตรงไหนก็ได้ในคำ คำคือตัวอักษร เครื่องหมายกำกับ และตัวเลข สิ่งอื่นใช้แยกคำเท่านั้น เรียงตามคะแนนรวม จากนั้นตามวันเผยแพร่จากใหม่ไปเก่า และตัวระบุรายการ model ที่ไซต์ปรับไว้ใน การตั้งค่า → ซอร์สโค้ดและการเผยแพร่ จะค้นหาเฉพาะฟิลด์ที่เลือก และจัดอันดับตามน้ำหนักสูงสุด 1 ถึง 10 ในบรรดาฟิลด์เหล่านั้นที่มีทุกคำ ชื่อเรื่องที่ตรงของ model ที่ไม่ได้ปรับนับเป็น 10 ฟิลด์ relation ตรงด้วยป้ายชื่อของเป้าหมายที่การอ่านสาธารณะให้บริการแก่ item ใน relations ซึ่งอ่านตอนค้นหา ป้ายชื่อฉบับร่างของเป้าหมายไม่ตรงเลย เป้าหมายที่เลิกเผยแพร่ อยู่ในถังขยะ หรืออยู่ใน model ส่วนตัวก็ไม่ตรงเช่นกัน และ summary ไม่มีป้ายชื่อของ relation เลย หากไม่มี q คำตอบจะว่างและบอกเพียงว่าการค้นหาทำงานที่นี่ คำตอบเหมือนกันสำหรับผู้เรียกทุกราย cache ไม่เกินห้านาทีและถูกล้างทุกครั้งที่เผยแพร่

พารามิเตอร์ จำเป็น ความหมาย
q ไม่ สิ่งที่จะค้นหา: 1 ถึง 200 ตัวอักษร ไม่เกิน 6 คำรวมคำซ้ำ คำละไม่เกิน 64 ตัวอักษร ใช้อัญประกาศคู่ค้นหาวลี ไม่สนตัวพิมพ์เล็กใหญ่
locale ไม่ ภาษาที่จะให้บริการ เป็นรหัสที่ตรงกับ ^[A-Za-z0-9_-]{1,64}$ ค่าเริ่มต้นคือภาษาเริ่มต้นของไซต์ สิ่งที่เกิดขึ้นเมื่อ item ไม่มีเนื้อหาในภาษานั้นถูกกำหนดโดยนโยบาย locale ของไซต์ ไม่ใช่โดยพารามิเตอร์นี้ ดู Locale ด้านล่าง
limit ไม่ จำนวนผลลัพธ์ต่อหน้า 1 ถึง 20 ค่าเริ่มต้น 10
cursor ไม่ page.next_cursor ของคำตอบก่อนหน้าสำหรับ q และ locale เดียวกัน ภายใต้การตั้งค่า กฎการจับคู่ ลำดับภาษาสำรอง และรุ่นของเนื้อหาที่เผยแพร่เดียวกัน
สถานะ ความหมาย
200 query, data: รายการ { kind, id, model, slug, locale, title, summary, title_parts?, summary_parts?, published_at } (kind เป็น page, post หรือ entry), page: { limit, has_more, next_cursor }, locale: { requested, default }
400 public_search_invalid: key ที่ไม่รู้จักหรือซ้ำ ค่าว่าง คำค้นเกินขอบเขต limit ไม่ถูกต้อง วลีว่างหรืออัญประกาศไม่ปิด หรือ cursor จากคำค้นอื่นหรือจากก่อนเปลี่ยนการตั้งค่า กฎการจับคู่ ลำดับภาษาสำรอง หรือรุ่นของเนื้อหาที่เผยแพร่
404 search_off: การค้นหาของไซต์ปิดอยู่ locale_unknown: ไซต์ไม่มีภาษานั้น unknown_host: ไม่มีไซต์บน host นี้
500 site_locale_policy_invalid: ไซต์ไม่มีนโยบาย locale ที่ใช้ได้เก็บไว้ ผู้ดูแลต้องตั้งค่าที่ การตั้งค่า → ภาษา ความล้มเหลวที่ไม่คาดคิดจะคืน Internal Server Error แบบข้อความธรรมดา
หน้าต่างเทอร์มินัล
curl "https://example.com/v1/_search?q=lighthouse&locale=en"
{ "query": "lighthouse", "data": [
{ "kind": "page", "id": "ci_1", "model": "page", "slug": "tours", "locale": "en",
"title": "Lighthouse tours", "summary": "Walk the cliffs at dawn.", "published_at": "2026-10-01T09:00:00.000Z" } ],
"page": { "limit": 10, "has_more": false, "next_cursor": null },
"locale": { "requested": "en", "default": "en" } }

ใช้เครื่องหมายอัญประกาศคู่เพื่อค้นหาวลี เช่น "blue harbour" ต้องพบคำติดกันตามลำดับภายในฟิลด์เดียวที่ใช้ค้นหา คำซ้ำยังนับตามจำนวนครั้งที่ระบุ อัญประกาศที่ไม่ปิดหรือวลีว่างจะถูกปฏิเสธ คำที่อยู่นอกวลีต้องตรงตามกฎการจับคู่ของโมเดลด้วย

คะแนนรวมเท่ากับคะแนนพื้นฐานบวก seo.search_priority ของเวอร์ชันที่เผยแพร่ ซึ่งมีค่าตั้งแต่ −10 ถึง 10 และมีค่าเริ่มต้นเป็น 0 ลำดับความสำคัญไม่ทำให้รายการที่ไม่ตรงคำค้นกลายเป็นผลลัพธ์ การแก้ในร่างไม่มีผลจนกว่าจะเผยแพร่ cursor ผูกกับรุ่นของเนื้อหาที่เผยแพร่ด้วย เมื่อรุ่นเปลี่ยนต้องเริ่มค้นหาใหม่

title_parts และ summary_parts เป็นข้อมูลเสริมในรูปแบบรายการ { text, match } เมื่อนำ text มาต่อกันจะได้ข้อความเดิม แสดงเป็นข้อความธรรมดาและครอบส่วนที่ตรงด้วย <mark> ของ renderer เอง คำตอบไม่มี HTML สำหรับนำไปแทรกโดยตรง

แนะนำขณะพิมพ์ รายการที่เผยแพร่อยู่ไม่เกิน limit รายการสำหรับคำค้นที่ยังพิมพ์อยู่ จากแถวเดียวกันและกฎเดียวกับ /v1/_search: หน้าที่เผยแพร่ โพสต์บล็อกบนไซต์บล็อก และ model สาธารณะที่เลือกไว้ ในภาษาที่ขอหรือภาษาแรกของลำดับ ไม่มีร่าง ภาษาที่ยังไม่เผยแพร่ ถังขยะ section ที่ใช้ร่วมกัน หรือ model ส่วนตัว ทุกคำใน q จับคู่ตามการจับคู่ของ model เหมือนใน /v1/_search ยกเว้นคำสุดท้ายที่พิมพ์ไม่ครบได้ ซึ่งตรงกับส่วนต้นของคำ (ตรงไหนก็ได้ในคำเมื่อเป็น substring) model ที่ปรับไว้จะตรงเฉพาะฟิลด์ที่เลือก คะแนนพื้นฐานคือ 2 เมื่อชื่อเรื่องขึ้นต้นด้วย q, 1 เมื่อชื่อเรื่องมีทุกคำ และ 0 เมื่อตรงเฉพาะในข้อความ จากนั้นบวกลำดับความสำคัญของเวอร์ชันที่เผยแพร่ แล้วเรียงตามรายการใหม่สุดและตัวระบุ วลีที่ปิดอัญประกาศแล้วต้องตรงครบทุกคำ ไม่ใช้กฎคำสุดท้ายที่ยังพิมพ์ไม่ครบ slug คือที่อยู่ที่เผยแพร่ในภาษาที่ให้บริการ เหมือนใน /v1/_search ไม่มีการบันทึกสิ่งใดเกี่ยวกับคำค้น คำตอบเหมือนกันสำหรับผู้เรียกทุกราย cache ไม่เกินห้านาทีและถูกล้างทุกครั้งที่เผยแพร่ ไซต์แบบ managed และไซต์ที่สร้างขึ้นเรียกผ่าน /search?suggest=<q>&locale=<code> ของไซต์เอง ซึ่งตอบ { data: [{ title, summary, title_parts?, summary_parts?, href, kind }] } โดยแต่ละที่อยู่เป็นแบบที่ไซต์นั้นลิงก์

พารามิเตอร์ จำเป็น ความหมาย
q ใช่ สิ่งที่กำลังพิมพ์: 2 ถึง 100 ตัวอักษร ไม่เกิน 6 คำรวมคำซ้ำ คำละไม่เกิน 64 ตัวอักษร ใช้อัญประกาศคู่ค้นหาวลี ไม่สนตัวพิมพ์เล็กใหญ่
locale ใช่ ภาษาที่จะให้บริการ เป็นรหัสที่ตรงกับ ^[A-Za-z0-9_-]{1,64}$ fallback เป็นไปตามนโยบาย locale ของไซต์ เหมือนใน /v1/_search
limit ไม่ จำนวนคำแนะนำที่จะคืน 1 ถึง 10 ค่าเริ่มต้น 5
สถานะ ความหมาย
200 query, data: รายการ { kind, id, model, slug, locale, title, summary, title_parts?, summary_parts?, published_at } (kind เป็น page, post หรือ entry), limit, locale: { requested, default }
400 public_search_invalid: ไม่มี q หรือ locale key ที่ไม่รู้จักหรือซ้ำ ค่าว่าง คำค้นเกินขอบเขต หรือ limit ไม่ถูกต้อง
404 search_off: การค้นหาของไซต์ปิดอยู่ locale_unknown: ไซต์ไม่มีภาษานั้น unknown_host: ไม่มีไซต์บน host นี้
500 site_locale_policy_invalid: ไซต์ไม่มีนโยบาย locale ที่ใช้ได้เก็บไว้ ผู้ดูแลต้องตั้งค่าที่ การตั้งค่า → ภาษา ความล้มเหลวที่ไม่คาดคิดจะคืน Internal Server Error แบบข้อความธรรมดา
หน้าต่างเทอร์มินัล
curl "https://example.com/v1/_search/suggest?q=light&locale=en"
{ "query": "light", "data": [
{ "kind": "page", "id": "ci_1", "model": "page", "slug": "tours", "locale": "en",
"title": "Lighthouse tours", "summary": "Walk the cliffs at dawn.", "published_at": "2026-10-01T09:00:00.000Z" } ],
"limit": 5, "locale": { "requested": "en", "default": "en" } }

อ่าน feed ของบล็อก โพสต์บล็อกที่เผยแพร่ล่าสุด 20 รายการในภาษาเดียว ตรงตามที่รายการบล็อกแสดง เพื่อให้ renderer เขียนเป็น RSS ที่ /rss.xml (ภาษาเริ่มต้น) และ /<code>/rss.xml summary คือคำอธิบายสำหรับการค้นหาของโพสต์ หรือส่วนต้นของข้อความ โพสต์จะออกจาก feed เมื่อเลิกเผยแพร่หรือย้ายไปถังขยะ cache ไม่เกินห้านาทีและถูกล้างทุกครั้งที่เผยแพร่

พารามิเตอร์ จำเป็น ความหมาย
locale ไม่ ภาษาที่จะให้บริการ เป็นรหัสที่ตรงกับ ^[A-Za-z0-9_-]{1,64}$ ค่าเริ่มต้นคือภาษาเริ่มต้นของไซต์ สิ่งที่เกิดขึ้นเมื่อ item ไม่มีเนื้อหาในภาษานั้นถูกกำหนดโดยนโยบาย locale ของไซต์ ไม่ใช่โดยพารามิเตอร์นี้ ดู Locale ด้านล่าง
สถานะ ความหมาย
200 data: { title, locale, default_locale, items } แต่ละ item คือ { id, slug, locale, title, summary, authors, date, published_at }
400 public_feed_invalid: key อื่นนอกจาก locale ซ้ำ หรือว่าง
404 feed_unavailable: ไซต์ไม่ใช่บล็อก หรือโพสต์ไม่เป็นสาธารณะ locale_unknown: ไซต์ไม่มีภาษานั้น
500 site_locale_policy_invalid: ไซต์ไม่มีนโยบาย locale ที่ใช้ได้เก็บไว้ ผู้ดูแลต้องตั้งค่าที่ การตั้งค่า → ภาษา ความล้มเหลวที่ไม่คาดคิดจะคืน Internal Server Error แบบข้อความธรรมดา
หน้าต่างเทอร์มินัล
curl "https://example.com/v1/_feed?locale=en"
{ "data": { "title": "Harbour notes", "locale": "en", "default_locale": "en", "items": [
{ "id": "ci_9", "slug": "first-light", "locale": "en", "title": "First light",
"summary": "Fog lifted at six.", "authors": ["Ana Lima"], "date": "2026-10-01",
"published_at": "2026-10-01T09:00:00.000Z" } ] } }

อ่านความคิดเห็นของรายการ ความคิดเห็นของผู้เยี่ยมชมที่อนุมัติแล้วในรายการที่เผยแพร่อยู่หนึ่งรายการของ model ที่รับความคิดเห็น (โพสต์บล็อก หรือ model ใดก็ได้ที่เปิดการตั้งค่าความคิดเห็น) ในภาษาของ item ที่ระบุ: ความคิดเห็นระดับบนสุดตามลำดับที่ขอ แต่ละความคิดเห็นมีการตอบกลับที่อนุมัติแล้วไม่เกิน 20 รายการ เรียงจากเก่าไปใหม่ ความคิดเห็นมีเพียง id ชื่อที่แสดง เว็บไซต์ ข้อความ เวลา และจำนวน reaction ไม่ส่งอีเมล สถานะการดูแล ผู้ที่กด reaction หรือสิ่งที่ใช้ป้องกันการใช้งานในทางที่ผิดเลย count นับความคิดเห็นและคำตอบที่อนุมัติแล้ว และหยุดที่ 999 (count_capped) ความคิดเห็นถูกซ่อนขณะที่รายการถูกยกเลิกการเผยแพร่หรืออยู่ในถังขยะ ขณะที่ model ไม่รับความคิดเห็น คำตอบระบุว่าปิดรับความคิดเห็น (open: false ไม่มี thread) ความคิดเห็นใหม่ส่งไปที่ POST /v1/interactions/comment.submit และ reaction ส่งไปที่ POST /v1/interactions/comment.react บน host ของไซต์ ซึ่ง intake Worker รับไว้ แคชไม่เกินห้านาทีและล้างทุกครั้งที่การดูแลเปลี่ยน ทุกการเผยแพร่ และทุกครั้งที่การตั้งค่าความคิดเห็นของ model เปลี่ยน

พารามิเตอร์ จำเป็น ความหมาย
entry ใช่ id ของ item รายการที่เผยแพร่แล้ว
order ไม่ oldest (ค่าเริ่มต้น), newest หรือ best: reaction helpful มากที่สุดก่อน จำนวนเท่ากันเรียงเก่าสุดก่อน จากนั้นตาม id
cursor ไม่ next_cursor ของหน้าก่อนหน้า cursor ใช้ได้เฉพาะกับรายการและลำดับที่มันมาจาก
limit ไม่ ความคิดเห็นระดับบนสุดต่อหน้า 1 ถึง 50 ค่าเริ่มต้น 20
สถานะ ความหมาย
200 data: { entry, locale, order, open, count, count_capped, threads, next_cursor } แต่ละ thread คือ { id, name, website, body, created_at, reactions, replies, more_replies } และแต่ละคำตอบคือ { id, name, website, body, created_at, reactions } reactions คือ { helpful }
400 public_comments_invalid: entry ขาดหรือผิดรูปแบบ order ที่ไม่รู้จัก มี key อื่น ค่าซ้ำหรือว่าง limit ไม่ถูกต้อง หรือ cursor จากรายการหรือลำดับอื่น
404 comments_unavailable: รายการไม่ได้เผยแพร่ในภาษาของมัน หรือ model เป็นแบบส่วนตัว ไม่แคชเลย
หน้าต่างเทอร์มินัล
curl "https://example.com/v1/_comments?entry=ci_9&order=best&limit=20"
{ "data": { "entry": "ci_9", "locale": "en", "order": "best", "open": true, "count": 2, "count_capped": false, "threads": [
{ "id": "cm_1", "name": "Ana", "website": null, "body": "Lovely light.", "reactions": { "helpful": 3 },
"created_at": "2026-10-02T08:00:00.000Z", "more_replies": false, "replies": [
{ "id": "cm_2", "name": "Bo", "website": "https://bo.example/", "body": "Agreed.",
"reactions": { "helpful": 0 }, "created_at": "2026-10-02T09:00:00.000Z" } ] } ], "next_cursor": null } }

route /v1 ทั้งสองคืน item ที่มีรูปร่างนี้:

คีย์ Type ความหมาย
id string id ของ item คงที่ข้ามการแก้ไขและการเปลี่ยนชื่อ
slug string หรือ null slug ของเวอร์ชันที่ส่งกลับ ซึ่งเป็นฉบับร่างเมื่อเปิดตัวอย่าง เป็น null เมื่อรายการไม่มีที่อยู่
model string slug ของ model
locale string locale ที่ใช้ให้บริการฟิลด์ ภายใต้นโยบาย fallback อาจต่างจากที่คุณขอ
version number หมายเลขเวอร์ชันที่บันทึกแล้วและส่งกลับ ซึ่งเป็นฉบับร่างเมื่อเปิดตัวอย่าง
published_at string หรือ null เวลาเผยแพร่แบบ ISO 8601
fields object ค่าของฟิลด์สำหรับ locale ที่ให้บริการ โดยใช้ field key เป็นคีย์ ฟิลด์ที่โมเดลไม่ได้แปลมีค่าเดียว ซึ่งให้บริการในทุก locale และใช้กับ sort และ filter ในทุก locale
relations object สำหรับการอ่านทั่วไปและลิงก์ตัวอย่างเก่าที่ไม่มีสิทธิ์อ่านฉบับร่างที่เกี่ยวข้อง: สำหรับแต่ละฟิลด์ relation รายการที่เรียงลำดับของ { instance_id, model, slug, display } ที่มีเฉพาะเป้าหมายที่ request นี้อ่านได้: เผยแพร่แล้ว ไม่อยู่ในถังขยะ และอยู่ใน model สาธารณะ ส่วนเป้าหมายใน model ส่วนตัวมีได้เฉพาะเมื่ออ่าน model ส่วนตัวด้วย token ที่ระบุทั้งสอง model slug และ display คือที่อยู่และป้ายชื่อที่เผยแพร่ของเป้าหมาย ซึ่งอ่านตอนนี้ display เป็น null เมื่อฟิลด์ป้ายชื่อเป็นส่วนตัว model คือ slug ของ model ของเป้าหมาย ฟิลด์ที่รับหลาย model จะส่งเป้าหมายของแต่ละ model ตามลำดับรายการ เป้าหมายอื่นจะถูกตัดออกเหมือนไม่ได้ลิงก์ไว้ เป้าหมายที่แปลแล้วให้บริการในภาษาของ item จากนั้นตามลำดับภาษาสำรองของไซต์ ภายใต้ strict เป้าหมายที่แปลแล้วซึ่งไม่มีเวอร์ชันที่เผยแพร่ในภาษานั้นจะถูกตัดออก เป้าหมายที่ไม่ได้แปลให้บริการในทุกภาษา ลิงก์ตัวอย่างใหม่ใช้ฉบับร่างล่าสุดที่บันทึกแล้วของเป้าหมายที่มีสิทธิ์อ่าน รวมถึงรายการที่ยังไม่เคยเผยแพร่ ที่อยู่และป้ายชื่อมาจากฉบับร่างเหล่านั้น ระบบตรวจสิทธิ์ปัจจุบันของผู้สร้างต่อรายการ ฟิลด์ และภาษา โดยตัดฟิลด์ส่วนตัว เป้าหมายที่อ่านไม่ได้ และเป้าหมายในถังขยะออก ดู ตัวอย่างฉบับร่าง
expanded object สำหรับการอ่านทั่วไปและลิงก์ตัวอย่างเก่าที่ไม่มีสิทธิ์อ่านฉบับร่างที่เกี่ยวข้อง: เฉพาะเมื่อใช้ expand สำหรับแต่ละฟิลด์ relation ที่ระบุ { data, page }: เป้าหมายไม่เกิน 25 รายการเป็น item และ page.next_cursor สำหรับส่วนที่เหลือที่ /v1/_related/:model ลิงก์ตัวอย่างใหม่เติมฟิลด์นี้ให้อัตโนมัติ ทั้งความสัมพันธ์ทั่วไปและความสัมพันธ์ในคอมโพเนนต์ ไม่เกินสี่ระดับและ 128 เป้าหมายรวมทั้งคำตอบของรายการหลัก เมื่อลิงก์วนซ้ำจะเหลือเพียงตัวระบุรายการ การขยายฉบับร่างไม่ใช้เคอร์เซอร์ความสัมพันธ์สาธารณะ
media object สำหรับแต่ละฟิลด์ media รายการที่เรียงลำดับของ { media_id, name, mime_type, kind, size, url, thumbnail_url, alt, width, height } kind คือ image, document, audio, video หรือ file แสดงภาพด้วย <img> เสียงและวิดีโอด้วย element ของมันพร้อมลิงก์ดาวน์โหลด และเอกสารเป็นลิงก์ที่มีชื่อ ประเภท และขนาด width และ height เป็น null สำหรับทุกอย่างที่ไม่ใช่ภาพ รายการยังมี crop ({ x, y, width, height }) และ focal ({ x, y }) เมื่อผู้แก้ไขกำหนดไว้ เป็นสัดส่วน 0–1 ของภาพที่เก็บไว้ ค่าเหล่านี้บอกวิธีแสดงภาพและไม่เคยกำหนดให้ไฟล์อื่น ไบต์และ url ไม่เปลี่ยน และไม่มีสำเนาที่ย่อขนาดหรือครอปให้บริการ ไฟล์ที่คัดลอกจาก media source ยังมี credit แบบอ่านอย่างเดียว: { author, source, license, url } (author และ url อาจเป็น null) ตามที่เป็นอยู่ตอนบันทึกรายการ ให้แสดงไว้ใต้ภาพ ไฟล์ส่วนตัวของโมเดลส่วนตัว (ใช้ได้เฉพาะที่นั่น) มี visibility: "private" พร้อม url ว่างและ thumbnail_url เป็น null ที่อยู่ของไฟล์ถูกซ่อน และไบต์มาจากการลงชื่อเข้าใช้ของแอปคุณเอง ไม่ใช่จาก API นี้

ฟิลด์ rich text เก็บ rich document หนึ่งฉบับ ในรูป HTML แบบ canonical: ย่อหน้า หัวข้อ รายการ คำพูดอ้างอิง บล็อกโค้ด เส้นคั่น ตาราง แกลเลอรีรูปภาพ บล็อกเสียง วิดีโอ และไฟล์ และการฝัง ทุกการเขียนจะถูกตรวจ และ markup ที่อยู่นอกชุดนี้จะถูกปฏิเสธ API จึงไม่เคยส่ง script, style, attribute ของ event หรือ frame บล็อกถูกเขียนเป็นข้อมูล ไม่ใช่ตัวเล่น: บล็อก media คือ <figure data-block="video|audio|file"> ที่มีลิงก์หนึ่งลิงก์พร้อม data-media-id, type และ data-size ส่วนการฝังคือ <figure data-block="embed" data-provider data-url data-title> ที่ไม่มี <iframe> data-provider คือ youtube, vimeo หรือ host ของ origin ที่ตั้งค่าไว้ และ data-url คือที่อยู่ frame แบบ https ที่เป็น canonical ส่ง origin จาก /v1/_embeds เพื่อแสดง frame ของ provider ที่ตั้งค่าไว้ หากไม่ส่งจะแสดงเป็นลิงก์ ให้แสดงผลแบบเดียวกับ renderer ของ Dee Wan เอง คือด้วย renderRichDocument จาก ข้อกำหนดข้อมูลหน้า (เว็บไซต์ Blog)

response ของ preview ยังมี preview_identity อาจมี preview_attestation และสำหรับ model ของหน้าจะมี mediaPresentation response ที่เผยแพร่แล้วไม่มีทั้งสามอย่างนี้เลย

แต่ละภาษาของ translation group เผยแพร่แยกกันและให้บริการเวอร์ชันที่เผยแพร่ของตัวเอง ดังนั้นรายการจะคืนหนึ่ง item ต่อหนึ่งกลุ่ม คือภาษาที่นโยบาย locale ให้บริการ จากเวอร์ชันที่เผยแพร่ของภาษานั้นเอง ภาษาที่ยังไม่เผยแพร่จะไม่ถูกให้บริการจากเวอร์ชันของภาษาอื่น

ฟิลด์ที่ตั้งเป็น Private ใน Model Builder จะไม่ถูกให้บริการเลย: ไม่อยู่ใน fields, relations และ media ของทุก item, item ที่ขยาย และหน้าที่เกี่ยวข้อง ไม่อยู่ในฟีดของบล็อกและการค้นหาในไซต์ ภายในค่าของ component ฟิลด์ component ที่เป็นส่วนตัวก็ไม่อยู่ด้วย query ที่ระบุฟิลด์ส่วนตัวใน filter[...], sort, fields, relation, expand หรือ cursor ของหน้าที่เกี่ยวข้อง จะได้การปฏิเสธแบบเดียวกับฟิลด์ที่ไม่มีอยู่ query จึงรู้ค่าของฟิลด์หรือแม้แต่ว่าฟิลด์มีอยู่ไม่ได้ กฎนี้ใช้กับ model ส่วนตัวที่อ่านด้วย token ด้วย

ให้บริการเฉพาะฟิลด์ที่ model ประกาศอยู่ตอนนี้ เวอร์ชันที่เผยแพร่ยังเก็บคีย์เดิมของฟิลด์ที่เปลี่ยนชื่อหรือลบไปแล้ว แต่ไม่มีการอ่านสาธารณะใดให้บริการค่าภายใต้คีย์นั้น: ไม่อยู่ใน fields, relations, media ภายในค่าของ component ในฟีดของบล็อก หรือในการค้นหาในไซต์ ฟิลด์ที่เคยเป็นส่วนตัวแล้วถูกเปลี่ยนชื่อหรือลบจึงไม่กลับมาภายใต้คีย์เดิม ฟิลด์จะใช้คีย์นั้นไม่ได้ตราบใดที่ยังมีเวอร์ชันที่เก็บไว้ซึ่งมีค่าภายใต้คีย์นั้น: การบันทึกโมเดลหรือคอมโพเนนต์ จะถูกปฏิเสธด้วย retained_key_reuse ค่าเก่าจึงไม่กลับมาภายใต้ฟิลด์ใหม่

model รายการเดียวมีได้ไม่เกินหนึ่งรายการ route ของมันเป็น route ปกติ: GET /v1/<model> ตอบรายการที่มีไม่เกินหนึ่ง item ใน envelope ปกติ และ GET /v1/<model>/<slug> ตอบ item นั้นตามที่อยู่ อ่านรายการเมื่อไม่รู้ที่อยู่

แต่ละไซต์มีนโยบาย locale ตั้งค่าที่ การตั้งค่า → ภาษา:

  • strict: item ถูกให้บริการเฉพาะใน locale ที่ขอ และเฉพาะเมื่อภาษานั้นเผยแพร่อยู่ รายการจะไม่รวม item ที่ไม่มี locale นั้น route ของ item ตอบ 404 locale_unavailable ภาษาสำรองจะไม่ถูกใช้
  • fallback: ภาษาที่ขอ แล้วจึงเป็นลำดับภาษาสำรองที่ขยายแล้ว หากลำดับไม่ลงท้ายด้วย หยุด จึงลองภาษาเริ่มต้นของไซต์และภาษาหลักของรายการ ระบบส่งเฉพาะภาษาที่เผยแพร่แล้วในลำดับนี้ ค่า locale ระบุภาษาที่ให้บริการจริง

ภาษาสำรองตั้งค่าแยกตามภาษาที่ การตั้งค่า → ภาษา → คำแปลที่ขาด ตามลำดับ หากไม่ได้ตั้งให้หยุด ระบบจะลองภาษาเริ่มต้นของไซต์และภาษาหลักของรายการหลังจากภาษาสำรอง ภาษาสำรองของภาษาสำรองจะตามมาต่อ: หากลำดับของภาษาที่ขอไม่ลงท้ายด้วย หยุด pt-BR → pt และ pt → es ให้บริการ pt-BR, pt, es แล้วจึงเป็นภาษาเริ่มต้น จากนั้นจึงลองภาษาหลักของรายการ วงวนและภาษาที่ไม่รู้จักจะถูกปฏิเสธตอนบันทึก

เปิด หยุดหลังจากลองภาษาเหล่านี้ สำหรับภาษาที่ขอได้ ระบบจะหยุดหลังลำดับภาษาสำรองที่กำหนดไว้ โดยไม่เพิ่มภาษาเริ่มต้นของไซต์หรือภาษาหลักของรายการ เช่น es-MX → es → หยุด กฎนี้ใช้กับภาษาที่ขอเท่านั้น ไม่เปลี่ยนลำดับของภาษาอื่นและไม่มีผลต่อนโยบาย strict ตัวเลือกหยุดของภาษาสำรองไม่หยุดลำดับของภาษาอื่นที่ขอ กฎเดียวกันใช้กับภาษาของรายการและภาษาสำรองของช่องที่ไม่บังคับกรอก

ผู้เรียก override นโยบายหรือภาษาสำรองไม่ได้

ทุก body ข้อผิดพลาดแบบ JSON มีรูปร่าง { "error": string, "reason"?: string } public_filter_too_many, public_filter_too_many_values, public_expand_too_many และ public_expand_too_deep เพิ่ม max ให้แยกกรณีตาม reason และตาม status ห้ามแยกตามข้อความของ error

Status reason เมื่อใด
400 public_filter_invalid query ที่ถูกปฏิเสธใด ๆ: พารามิเตอร์ที่ไม่รู้จักหรือใส่ซ้ำ, ค่าว่าง, limit, order, locale, filter หรือ cursor ที่ไม่ถูกต้อง
400 public_filter_too_many คู่ relation/tag มากกว่า 16 คู่ หรือ filter บนฟิลด์มากกว่า 4 ฟิลด์ max อยู่ใน body
400 public_filter_too_many_values ค่ามากกว่า 10 ค่าสำหรับ filter[field] เดียว max อยู่ใน body
400 public_filter_type_unsupported filter บนฟิลด์ที่ชนิดเปรียบเทียบไม่ได้ ช่วงบนชนิดที่ไม่มีช่วง หรือ relation หรือ tag บนฟิลด์ relation ที่รับหลาย model
400 public_cursor_mismatch cursor ที่ส่งมากับ query ไซต์ model หรือชุด model ของ token ที่ไม่ใช่ของมัน
400 public_cursor_stale cursor ของ /v1/_related/:model ที่ item ถูกเผยแพร่อีกครั้ง
400 public_expand_invalid expand ระบุฟิลด์ที่ไม่ใช่ฟิลด์ relation ของ model ฟิลด์ relation ที่รับหลาย model หรือระบุซ้ำ
400 public_expand_too_many expand ระบุมากกว่า 3 ฟิลด์ max อยู่ใน body
400 public_expand_too_deep expand ระบุ path ที่มีจุด ขยายได้ระดับเดียว max เป็น 1
404 unknown_host ไม่มีไซต์ที่ active บน host นี้
404 locale_unavailable item ไม่มีเนื้อหาที่นโยบาย locale จะให้บริการ
404 ambiguous_slug item ที่เผยแพร่แล้วสองรายการอ้าง slug นี้
404 ไม่มี ไม่มี model นี้, ไม่มี item ที่เผยแพร่แล้วนี้, preview token ที่ล้มเหลว หรือ model ส่วนตัวที่ไม่มี token ที่ถูกต้อง
422 reason ของ media presentation เฉพาะ preview ของหน้า ดู route ของ item ด้านบน
500 site_locale_policy_invalid นโยบาย locale ของไซต์หายไปหรือไม่ถูกต้อง หรือภาษาสำรองที่บันทึกไว้เสียหาย

กฎการตรวจสอบสำหรับ route /v1 ทั้งสอง:

  • พารามิเตอร์ที่ไม่รู้จักจะถูกปฏิเสธ ไม่ถูกละเลย
  • ค่าว่าง (?locale=) จะถูกปฏิเสธ
  • พารามิเตอร์หนึ่งปรากฏได้ครั้งเดียว ยกเว้น relation, tag และ filter[field] (เฉพาะการเท่ากัน) บน route ของรายการ
  • รหัส locale และ field key ของ filter ต้องตรงกับ ^[A-Za-z0-9_-]{1,64}$
  • ค่า filter (ส่วนหลัง : ตัวแรก) ยาวไม่เกิน 255 ตัวอักษร และ ทั้งสองฝั่งของ : ต้องไม่ว่าง
  • route ของ item รับเฉพาะ locale, preview และ expand

การเปิดเผยโมเดลใน API สาธารณะและตัวควบคุมโทเค็น

model เป็นแบบสาธารณะหรือส่วนตัว ตั้งค่าที่ การตั้งค่า → API model ส่วนตัว อ่านได้เฉพาะเมื่อมี site API token:

Authorization: Bearer dw_site_…

token สร้างที่ การตั้งค่า → API แต่ละ token ระบุ model ที่มันอ่านได้ แสดงเพียงครั้งเดียว มีวันหมดอายุได้ถ้าต้องการ และเพิกถอนได้ที่นั่นทุกเมื่อ การเพิกถอนและการหมดอายุมีผลกับ request ถัดไป token ขึ้นต้นด้วย dw_site_ หลังจากผู้ดูแลหมุนเวียน signing key ไปยังรุ่นที่สอง token ใหม่ จะขึ้นต้นด้วย dw_site2_ public Worker ต้องมีการตั้งค่า signing key ที่ถูกต้อง (SITE_TOKEN_HMAC_KEY) หากไม่มี จะไม่มี model ส่วนตัวใดอ่านได้

request ที่ไม่มี token และ request ที่มี token ผิดจะได้คำตอบเดียวกัน (SAME): 404 ไม่มี reason เหมือนกับ model ที่ไม่มีอยู่ นี่เป็นความตั้งใจ: การแยกความแตกต่างจะบอกผู้เรียกที่กำลังเดาว่า model นั้นมีอยู่ ดังนั้นเมื่อ model ส่วนตัวตอบ 404 ให้ตรวจ token ก่อนตรวจ URL

model สาธารณะไม่สนใจ header Authorization

ชื่อ ขอบเขตโมเดล และวันหมดอายุของโทเค็น API ก่อนออกโทเค็น

response จะ cache ได้หรือไม่ขึ้นกับ model ไม่ใช่ request:

  • model สาธารณะ, การอ่านที่เผยแพร่แล้ว: cache ได้ที่ edge เมื่อเปิด caching เป็นเช่นนี้ไม่ว่า request จะมี header Authorization หรือไม่
  • model ส่วนตัว หรือ preview ที่ให้บริการแล้ว: เป็น Cache-Control: private, no-store เสมอ ไม่ถูกเก็บเลย preview ที่ให้บริการแล้วคือ preview ที่ token ตรวจสอบผ่าน: item ของมัน และการปฏิเสธ 422 เรื่องการแสดงสื่อของมัน preview token ที่ตรวจสอบไม่ผ่านบน model สาธารณะจะได้ 404 สาธารณะตามปกติ
  • ข้อผิดพลาด 400 และ 500 site_locale_policy_invalid: no-store

caching ปิดไว้โดยค่าเริ่มต้น: PUBLIC_CACHE_MAX_AGE เป็น 0 และ response สาธารณะ บอก no-store ตั้ง PUBLIC_CACHE_MAX_AGE เป็นจำนวนวินาที ไม่เกิน 31536000 (หนึ่งปี) บนทั้ง public และ admin Worker เพื่อเปิดใช้ จากนั้นการ publish หรือ unpublish จะ purge response ที่ได้รับผลกระทบตาม cache tag การ purge นั้นต้องมี CF_PURGE_ZONE_ID และ CF_PURGE_API_TOKEN (token ที่มี Zone → Cache Purge) บน admin Worker หากไม่มี การเปลี่ยนแปลงจะรายงานว่า การ purge ถูกข้าม และ response ที่ cache ไว้จะคงอยู่จนหมดอายุ

Response header:

Header model สาธารณะ model ส่วนตัว หรือ preview ที่ให้บริการแล้ว
Cache-Control no-store เมื่อปิด caching public, max-age=<min(60, TTL)>, s-maxage=<TTL> เมื่อเปิด private, no-store
X-Cache MISS จาก Worker, HIT จาก edge cache BYPASS
Cache-Tag site:<id>, model:<slug>, บน route ของ item instance:<id> และ model:<slug> ของ model ที่ลิงก์จากฟิลด์ component หรือที่ขยายด้วย expand ไม่ส่ง
Vary Accept-Encoding Authorization

cache key คือ host บวก path และ query ที่ normalize แล้ว ดังนั้น query เดียวกัน ที่เขียนพารามิเตอร์ต่างลำดับกันจะใช้รายการเดียวกัน สำหรับ model ที่มีฟิลด์ส่วนตัว key ยังระบุฟิลด์เหล่านั้นด้วย จึงไม่ใช้คำตอบที่แคชไว้ก่อนเปลี่ยนฟิลด์เป็นส่วนตัว การเปลี่ยนว่าฟิลด์ใดเป็นส่วนตัวยังล้าง model:<slug> และการค้นหาในไซต์ด้วย

คงที่ (STABLE) ภายใน v1: path, ชื่อพารามิเตอร์, ความหมายของ status code และค่า reason และรูปร่างระดับบนสุดของ response: contracts, data และบน route ของรายการคือ object page ที่มี limit, has_more และ next_cursor คีย์ของ item เองคือ id, slug, model, locale, version, published_at, fields, relations และ media ส่วน item ที่เป็นหน้ามี page_contract ด้วย เมื่อใช้ expand item มี expanded ด้วย item ที่เวอร์ชันเผยแพร่กำหนดค่าแทนที่ สำหรับการค้นหาและการแชร์ในภาษาที่ให้บริการจะมี seo ด้วย: { format: 1, title, description, image, canonical, robots, schema_type, search_priority? } โดย image คือ { media_id, url, alt, width, height } หรือ null, canonical คือที่อยู่ https หรือ path บนไซต์นี้หรือ null, robots คือ index หรือ noindex และ schema_type คือ WebPage, AboutPage, ContactPage, Article หรือ null (ข้อมูลแบบมีโครงสร้างของ renderer ใช้ค่านี้ระบุประเภทหน้า) ให้บริการเฉพาะค่าของเวอร์ชันที่เผยแพร่แล้ว การเปลี่ยนในร่างจะไม่ถูกให้บริการ search_priority เป็นข้อมูลเสริมจำนวนเต็มตั้งแต่ −10 ถึง 10 ค่าเริ่มต้น 0 ใช้จัดอันดับการค้นหาของไซต์เท่านั้น ไม่สร้างแท็กในส่วน head ส่วนเพิ่มเติมจากปลั๊กอินที่เก็บตามเวอร์ชันอาจเพิ่ม seo.renderer_metadata ซึ่งมีเมทาแท็ก ลิงก์ และ JSON-LD ที่ประกาศไว้ พร้อมรหัสปลั๊กอินและรหัสส่วนเพิ่มเติม ค่า SEO หลักมีลำดับความสำคัญสูงกว่า ลำดับชั้นที่เผยแพร่อาจเพิ่ม ancestors[field] และ seo.breadcrumbs[field] เป็นรายการ { id, locale, label, url } ตามลำดับ โดยใช้ชื่อที่เผยแพร่ล่าสุด เส้นทางที่ยอมรับ และกฎภาษาของไซต์ ไม่รวมชื่อจากฉบับร่างหรือรายการบรรพบุรุษที่เข้าถึงไม่ได้ แต่ละสายมีบรรพบุรุษที่ใกล้ที่สุดได้สูงสุด 32 รายการ และคืนค่าสำหรับฟิลด์ลำดับชั้นสาธารณะ 8 ฟิลด์แรกตามชื่อคีย์ หากไม่มี seo หมายถึงไม่มีทั้งค่า SEO เฉพาะรายการและข้อมูลเพิ่มเติมที่คำนวณได้

รายการที่อ้างถึงไฟล์สาธารณะในคลังอาจมี mediaMetadata โดยใช้รหัสไฟล์เป็นคีย์ ค่าข้างในมีเฉพาะฟิลด์ไฟล์ที่เลือกให้เป็นสาธารณะ และอ้างอิง revision ปัจจุบันของไฟล์ แยกจากเวอร์ชันเนื้อหาที่เผยแพร่ เมื่อเปลี่ยนการเปิดเผยฟิลด์ คีย์แคชสาธารณะจะเปลี่ยนด้วย โปรเจกต์ที่สร้างออกไปเก็บภาพข้อมูลสาธารณะนี้ ณ เวลาที่สร้าง

เวอร์ชันของ ข้อกำหนดข้อมูลหน้า (PAGE CONTRACT VERSIONS): contracts.page.served คือ คำศัพท์ของหน้า (page vocabulary) ที่สร้าง response นี้ ปัจจุบันคือ 5 page_contract.required ของ item ที่เป็นหน้าคือเวอร์ชัน ข้อกำหนดข้อมูลหน้า ที่ renderer ต้องอ่านได้เพื่อ render หน้านั้น ปัจจุบันทุกหน้าที่อ่านได้ตอบด้วยเวอร์ชันที่ให้บริการ ดังนั้น required เท่ากับ contracts.page.served มันไม่ใช่ format ของหน้าที่จัดเก็บ ซึ่งเป็นตัวเลขอีกตัวหนึ่ง renderer ที่สร้างสำหรับเวอร์ชัน N อ่านทุกหน้าที่ required ไม่เกิน N และปฏิเสธ หน้าที่เหลือทั้งหน้า มันไม่ render หน้าเพียงบางส่วน เมื่อใช้ fields= จะคง page_contract ไว้เฉพาะเมื่อเลือก slides

ไม่คงที่ (NOT stable) และห้าม parse: เนื้อหาของ next_cursor, ข้อความของ error และลำดับของคีย์ พารามิเตอร์ใหม่ที่ไม่บังคับ, reason ใหม่ หรือ คีย์ใหม่ภายใน item อาจปรากฏภายใน v1 ไม่มีสิ่งใด ที่บันทึกไว้ที่นี่จะเปลี่ยนความหมายโดยไม่มีเวอร์ชันใหม่ใน path

ใช้ตัวกรอง การเลือกฟิลด์ และข้อมูลที่เกี่ยวข้องร่วมกัน

หัวข้อที่มีชื่อว่า “ใช้ตัวกรอง การเลือกฟิลด์ และข้อมูลที่เกี่ยวข้องร่วมกัน”

API ของ CMS ที่ทำงานอยู่ใช้ภาษาคำขอของตนเอง ไม่รับ where, select, include, orderBy, skip หรือ take ของ Prisma สำหรับ API ที่ดีพลอยและใช้อาร์กิวเมนต์ Prisma ดู การอ่านข้อมูลจาก API แอป

ตัวอย่างนี้สมมติว่ามีโมเดล article พร้อม kind ชนิดข้อความหรือตัวเลือก, price ชนิดตัวเลข, publication_date ชนิดวันที่ และฟิลด์ความสัมพันธ์ categories, authors ที่แต่ละฟิลด์รับโมเดลเป้าหมายชนิดเดียว ใช้คีย์ฟิลด์จริงของไซต์คุณ

หน้าต่างเทอร์มินัล
curl --get 'https://content.example.com/v1/article' \
--data-urlencode 'locale=en' \
--data-urlencode 'filter[kind]=guide' \
--data-urlencode 'filter[kind]=news' \
--data-urlencode 'filter[price][gte]=10' \
--data-urlencode 'filter[price][lt]=50' \
--data-urlencode 'tag=categories:architecture' \
--data-urlencode 'sort=-publication_date' \
--data-urlencode 'fields=title,price,publication_date' \
--data-urlencode 'expand=authors' \
--data-urlencode 'limit=10'

คำขอนี้อ่านคู่มือ หรือ ข่าวที่เผยแพร่แล้ว ราคาอย่างน้อย 10 แต่น้อยกว่า 50 และเชื่อมกับหมวดที่ผู้เรียกอ่านได้ซึ่งมี slug ที่เผยแพร่ว่า architecture เงื่อนไขคนละฟิลด์และความสัมพันธ์เชื่อมกันแบบ AND ผลลัพธ์มีฟิลด์ที่เลือก ตัวระบุรายการที่จำเป็น และผู้เขียนที่เผยแพร่แล้วและอ่านได้ใน expanded.authors.data เรียงตามค่า publication_date จากใหม่ไปเก่า ค่าที่หายไปอยู่ท้ายสุดและใช้ ID ตัดสินเมื่อค่าเท่ากัน การขยายความสัมพันธ์ไม่ทำให้ฉบับร่างกลายเป็นสาธารณะ

รายการหลักใช้ page.next_cursor ของตน แต่ละความสัมพันธ์ที่ขยายมี page.next_cursor แยกสำหรับ /v1/_related/:model อย่าใช้สลับกัน เมื่อตามหน้าหลักให้ส่งเคอร์เซอร์เดิมโดยไม่แก้ไขพร้อมพารามิเตอร์เดิม เปลี่ยนได้เฉพาะ limit ค่า has_more ระบุว่ายังมีหน้าต่อ ไม่ใช่จำนวนรวม

ขอบเขตต่อคำขอ: ตัวกรอง 4 ฟิลด์ ทางเลือกค่าที่เท่ากัน 10 ค่าต่อฟิลด์ ความยาวค่าตัวกรอง 255 ตัวอักษร คู่ relation/tag 16 คู่ คีย์ฟิลด์ที่เลือก 32 คีย์ ความสัมพันธ์ที่ขยาย 3 ฟิลด์ และเป้าหมายที่แนบมา 25 รายการต่อฟิลด์ ขยายได้ระดับเดียว ไม่รับ path แบบมีจุดหรือคำขอ Prisma ซ้อนตามอำเภอใจ ดูกฎตรวจสอบและเหตุผลการปฏิเสธที่แน่นอนในตาราง endpoint ด้านบน

รันบนเซิร์ฟเวอร์หรือระหว่างบิลด์ คำขอจากเบราว์เซอร์ต้องมีต้นทางที่อนุญาต เก็บโทเค็นส่วนตัวไว้บนเซิร์ฟเวอร์

async function* everyArticle(origin, locale = 'en') {
let cursor;
do {
const url = new URL(`${origin}/v1/article`);
url.searchParams.set('locale', locale);
url.searchParams.set('limit', '50');
// The cursor is opaque. Send back exactly what you were given; do not
// decode it, and do not construct one.
if (cursor) url.searchParams.set('cursor', cursor);
const response = await fetch(url);
if (!response.ok) throw new Error(`${response.status} from ${url}`);
const body = await response.json();
yield* body.data;
cursor = body.page.has_more ? body.page.next_cursor : undefined;
} while (cursor);
}
หน้าต่างเทอร์มินัล
curl -H "Authorization: Bearer $DEE_WAN_SITE_TOKEN" \
'https://example.com/v1/pricing'

เก็บ token ไว้บนเซิร์ฟเวอร์ ทุกอย่างที่ส่งไปยังเบราว์เซอร์เป็นสาธารณะ

ดาวน์โหลด dee-wan-client.mjs จาก การตั้งค่า → API แล้ว commit ไว้ ต้องใช้ Node 18 ขึ้นไป ไม่ต้องใช้อย่างอื่น ดึง client ด้วย site API token

หน้าต่างเทอร์มินัล
DEE_WAN_SITE_TOKEN=dw_site_… node dee-wan-client.mjs pull \
--url https://example.com --out src/dee-wan-public.ts

ก่อน deploy ให้ตรวจว่า client ที่ commit ไว้ยังตรงกับเว็บไซต์

หน้าต่างเทอร์มินัล
DEE_WAN_SITE_TOKEN=dw_site_… node dee-wan-client.mjs check --file src/dee-wan-public.ts

check จบด้วยรหัส 1 เมื่อมีการเปลี่ยนแบบ breaking ได้แก่ ลบโมเดล ฟิลด์ หรือภาษา เปลี่ยนชนิดฟิลด์ โมเดลกลายเป็นส่วนตัว ฟิลด์กลายเป็นไม่บังคับ มีค่า select ใหม่ หรือภาษาเริ่มต้นหรือนโยบายภาษาเปลี่ยน รหัส 0 คือไม่มีอะไรเปลี่ยนหรือทุกการเปลี่ยนแปลงเข้ากันได้ เช่น เพิ่มโมเดล ฟิลด์ หรือภาษา และจะแสดง รายการเหล่านั้น รหัส 2 คือตรวจไม่ได้ หลังมีการเปลี่ยนแปลง ให้รัน pull แล้ว commit ผลลัพธ์

client มี response envelope, locale union, ชนิดฟิลด์ที่แน่นอน, query type ที่รู้จักโมเดล และหนึ่งเมธอดต่อหนึ่งโมเดล ใน การตั้งค่า → API ยังคัดลอกหรือดาวน์โหลดด้วยมือได้ Dee Wan ไม่แก้ไขรีโพซิทอรีของคุณ

type RelationTarget = { instance_id: string; model: string; slug: string | null; display: string | null };
type Media = {
media_id: string;
name: string;
mime_type: string | null;
kind: 'image' | 'document' | 'audio' | 'video' | 'file';
size: number | null;
url: string;
thumbnail_url: string | null;
alt: string | null;
width: number | null;
height: number | null;
visibility?: 'private';
};
type Item<F> = {
id: string;
slug: string | null;
model: string;
locale: string;
version: number;
published_at: string | null;
fields: F;
relations: Record<string, RelationTarget[]>;
media: Record<string, Media[]>;
};
type Page<F> = {
contracts: { page: { served: number } };
data: Item<F>[];
page: { limit: number; has_more: boolean; next_cursor: string | null };
};
type ApiError = { error: string; reason?: string; max?: number };
type Article = { title: string; body: string };
const response = await fetch(url);
if (!response.ok) throw new Error(`${response.status} from ${url}`);
const page: Page<Article> = await response.json();

ค่า point: { "latitude": 13.75, "longitude": 100.5 } พิกัดเป็นจำนวนจำกัดในหน่วยองศา รัศมีใช้เมตร

GET /v1/places?filter[location][within_radius]=13.75,100.5,2000
GET /v1/places?filter[location][within_bbox]=13.5,100.2,14,100.8
GET /v1/places?filter[location][within_polygon]=<URL-encoded GeoJSON Polygon>
GET /v1/places?sort=location&origin=13.75,100.5

กรอบเรียงเป็น ใต้ ตะวันตก เหนือ ตะวันออก รวมขอบ ถ้าตะวันตกมากกว่าตะวันออกจะข้ามเส้นแอนติเมอริเดียน รัศมีและการเรียงใกล้ใช้ระยะ Haversine แบบตรง sort=-location เรียงไกลก่อน ค่าว่างอยู่ท้าย ใช้ ID ตัดสินระยะเท่ากัน cursor ผูกกับจุดเริ่มและกรอบ สิทธิ์ฟิลด์และโมเดลยังมีผล

สำหรับ within_polygon ให้ส่ง GeoJSON Polygon ที่เข้ารหัส URL เช่น {"type":"Polygon","coordinates":[[[100,13],[101,13],[101,14],[100,14],[100,13]]]} พิกัดเรียงเป็นลองจิจูด ละติจูด รวมขอบนอก แต่ไม่รวมพื้นที่ช่องว่างภายในและขอบของช่องว่าง รองรับรูปหลายเหลี่ยมเว้า จำกัดวงแหวนปิด 16 วง รวมไม่เกิน 256 คู่พิกัดโดยนับจุดปิดด้วย และข้อมูลขาเข้าไม่เกิน 16 KiB ระบบปฏิเสธวงแหวนที่ผิดรูป ไม่มีพื้นที่ ตัดตัวเอง หรือมีการตัดกันที่ไม่ถูกต้อง ขอบเป็นเส้นตรงบนระนาบลองจิจูด/ละติจูด ไม่ข้ามเส้นเมริเดียน 180 องศาให้อัตโนมัติ ไม่รับ MultiPolygon ตัวกรองทำงานก่อนนับผลลัพธ์และแบ่งหน้า ภายใต้กฎสิทธิ์เดิม

GET /v1/_sync?limit=50 เริ่ม snapshot แบ่งหน้าของรายการที่เผยแพร่และสื่อสาธารณะ model และ locale จำกัดรายการ ตาม next_cursor จนได้ checkpoint ครั้งถัดไปส่ง cursor=<checkpoint> ใช้โทเคนไซต์และ CORS เดิม โทเคนที่มีสิทธิ์อ่านโมเดลส่วนตัวรวมโมเดลนั้นได้ ไม่รวมสื่อส่วนตัว

ใช้ data ของ snapshot ก่อน แล้วใช้ changes ตามลำดับที่ได้รับ แต่ละรายการมี operation: upsert|delete, entity: entry|media, id และภาษาของรายการ upsert มี item ค่า source_site_id และ source_sequence ระบุเหตุการณ์ใน journal ของแหล่งข้อมูล ส่วน sequence เป็นเลขลำดับของแหล่งนั้น ไม่ใช่เลขลำดับร่วมของทุกฐานข้อมูล projection: true หมายถึงรายการแม่ถูกส่งมาใหม่หลังเนื้อหาที่เชื่อมโยงเปลี่ยน ID อาจซ้ำได้ จึงต้องประมวลผลซ้ำโดยไม่สร้างผลซ้ำ เก็บ checkpoint ที่ได้รับ อย่าคำนวณจาก sequence ที่มากที่สุด อย่าใช้ data และ deleted เป็นคนละชุด เพราะอาจเผยแพร่อีกครั้งหลังลบ

cursor เป็นค่าทึบที่ลงลายเซ็น ให้ส่งต่อโดยไม่แก้ไข การเผยแพร่หรือถอนเผยแพร่เนื้อหาที่เชื่อมโยงตามปกติ รวมถึงแหล่งเนื้อหาร่วม จะดำเนินต่อใน stream เดิม โดยส่งรายการแม่ที่มีสิทธิ์อ่านและได้รับผลกระทบมาใหม่ การเปลี่ยนรายการอื่นในโมเดลเป้าหมายไม่ทำให้ต้องเริ่ม snapshot ใหม่ งานตรวจ dependency แบ่งเป็นชุด หน้าอาจไม่มี changes แต่ยังมี next_cursor ให้ตามต่อ 409 reset_required ยังต้องเริ่ม snapshot ใหม่เมื่อสิทธิ์ ความเป็นส่วนตัว นโยบายเผยแพร่ นิยามคอมโพเนนต์ สิทธิ์แบ่งปัน รุ่นฐานข้อมูลต้นทาง หรือขอบเขตการเก็บ journal เปลี่ยน การเริ่ม process ใหม่ยังใช้ checkpoint เดิมได้ ฟิลด์ความสัมพันธ์ที่ซ่อนอยู่และโมเดลเป้าหมายที่อ่านไม่ได้ไม่เพิ่ม dependency ถ้าฐานข้อมูลต้นทางที่ใช้อยู่ไม่พร้อม จะได้ 503 sync_dependency_unavailable ให้ลอง cursor เดิมอีกครั้ง แหล่งข้อมูลที่ไม่ได้ใช้งานไม่ทำให้ stream หยุด หนึ่ง stream รองรับฐานข้อมูลต้นทางที่ใช้จริงได้ไม่เกิน 32 ฐานและ cursor ที่เข้ารหัสแล้วไม่เกิน 12 KiB ถ้าขอบเขตกว้างเกินไปจะได้ 400 sync_source_limit ให้แยก stream ตามโมเดล ไม่มีการล้าง journal อัตโนมัติ journal เก็บ ID และคำสั่ง ไม่เก็บเนื้อหาร่างหรือเนื้อหาฟิลด์ส่วนตัว

ลิงก์ร่วมอยู่ใน shared[field] เป็น { site_id, id, item } ถ้าเป้าหมายใช้ไม่ได้จะเป็น item: null ทุกคำตอบตรวจสิทธิ์และฟิลด์สาธารณะที่เผยแพร่ของต้นทางอีกครั้ง

หนึ่งคำตอบ resolve ได้ไม่เกิน 128 คู่ grant และภาษาที่ต่างกัน การอ้างอิงซ้ำ รวมถึง sync data และ changes ใช้ข้อมูลปัจจุบันชุดเดียวกัน ถ้าได้ 422 shared_reference_limit ให้ลด limit ของรายการหรือ sync แล้วลองอีกครั้ง

การเปลี่ยนเนื้อหาที่เผยแพร่ใด ๆ บนไซต์ต้นทางที่อ้างอิงอาจทำให้ checkpoint ใช้ไม่ได้ด้วย รวมถึงการเปลี่ยนค่าที่แสดงจากเนื้อหาสาธารณะที่เกี่ยวข้อง


ยังไม่เปิดตัว (pre-MVP) และยังไม่มีเวอร์ชันเสถียร
คอมมิตซอร์สโค้ด: e207e63239a627fbd2ae9c414ef7c6eb19fcfe5a (มีการแก้ไขที่ยังไม่คอมมิต)
วันที่สร้างเอกสาร (UTC): 2026-10-21T09:28:24.214Z
คอมมิตสำหรับอัปเกรด: ระบุไม่ได้: มีการแก้ไขที่ยังไม่คอมมิต. บันทึกการเปลี่ยนแปลง