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

โปรโตคอล v1

ทุก route, header, body, รหัส outcome และกฎการ retry ของ remote plugin protocol ปลั๊กอินคือ service ที่ deploy แยกต่างหาก: มันสื่อสารกับ Dee Wan ผ่าน HTTP และไม่รันโค้ดใดภายใน Dee Wan ดูเพิ่มเติมที่ การเขียนปลั๊กอิน สำหรับปลั๊กอินแรกตั้งแต่ต้นจนจบ, ความปลอดภัยของปลั๊กอิน สำหรับขอบเขตความเชื่อถือ และ การทดสอบปลั๊กอิน สำหรับวิธีทดสอบปลั๊กอิน

เวอร์ชันของ protocol ที่รองรับ: 1 เวอร์ชันจะถูกตกลงกันครั้งเดียวตอนติดตั้ง และถูก pin ไว้กับ การติดตั้งปลั๊กอินรายการนั้น การอัปเกรดที่จะเปลี่ยนเวอร์ชันนี้จะถูกปฏิเสธ (protocol_changed): การย้าย installation ไปยัง v2 หมายถึงการถอนการติดตั้งแล้วติดตั้ง release ใหม่ การเปลี่ยนแปลงที่ breaking ต่อ route, ความหมาย หรือรหัส error ต้องใช้ protocol v2 ฟิลด์ใน response ที่เพิ่มเข้ามาได้รับอนุญาตเพียงเพราะ client ถูกกำหนดให้ต้องเพิกเฉยต่อ key ใน response ที่ไม่รู้จัก shape ของ request เป็นแบบปิด — key ที่ไม่รู้จักจะถูกปฏิเสธ ไม่ใช่ถูกเพิกเฉย

ทุก route ของปลั๊กอินรับ Authorization: Bearer <token> โดย token คือ dwp_ ตามด้วยอักขระ URL-safe 43 ตัวจากความสุ่ม 256 บิต core เก็บเพียง hash SHA-256 ของมันและไม่สามารถแสดงมันได้อีก ปลั๊กอินได้รับมันเพียงครั้งเดียวตอน activation

route เหล่านี้อยู่ใต้ /api/plugin/v1 พวกมันไม่ต้องการ header Origin และไม่ต้องการ cookie เพราะ server ไม่ได้ส่งทั้งสองอย่าง core resolve installation และไซต์ ก่อน ที่จะอ่าน body ของ request ใด ๆ ตามลำดับนี้: ปฏิเสธไซต์หรือ ฐานข้อมูลที่ผู้เรียกระบุชื่อมา, resolve token ไปยัง installation ที่ active หนึ่งตัว, ใช้ rate limit, resolve ไซต์, route ไปยังฐานข้อมูลของ tenant

ผลที่ควรระบุให้ชัด:

  • ไซต์ได้มาจาก token ปลั๊กอินระบุไซต์เองไม่ได้ และการส่ง X-Site-Id มาเลยคือ invalid_request
  • cookie ของผู้ใช้ไม่ใช่การยืนยันตัวตนของปลั๊กอิน และ token ของปลั๊กอินจะไม่ถูกยอมรับบน route ด้านการดูแลระบบใดเลย
  • installation ที่เป็น pending, disabled และ revoked จะตอบ plugin_unauthorized ก่อนที่จะมี การเข้าถึง tenant ใด ๆ ทุก installation บนไซต์ที่ไม่ active ก็เช่นกัน
Header ที่ไหน ความหมาย
Authorization: Bearer <token> ทุก request ของปลั๊กอิน token ของ installation บังคับ
Idempotency-Key transition, การเขียน, draft, media และข้อเสนอ; การเตรียมเวอร์ชันอนาคตใช้การติดตั้ง รีลีส และรายการเดิมแทนคีย์ บังคับ อักขระ 1 ถึง 128 ตัวในช่วง \x21–\x7e (ASCII ที่พิมพ์ได้ ไม่มีช่องว่าง) เก็บไว้ 30 วัน /launch/exchange ไม่ใช้
Content-Type: application/json request ที่มี body รูปแบบ body เดียวที่รองรับ
X-Site-Id ไม่เคย ถูกปฏิเสธ ไซต์มาจาก token
Retry-After บนคำตอบที่ลองใหม่ได้ core ส่งค่าเป็นวินาทีเมื่อสถานะเป็น 429 ไคลเอนต์รับวันที่ HTTP ในคำตอบขาเข้าที่ลองใหม่ได้ด้วย ให้รอตามค่าที่ระบุภายในขีดจำกัดจำนวนครั้ง อายุ และระยะเวลารอ
Referrer-Policy: no-referrer response ของ launch จาก core และหน้าจัดการของคุณ core ตั้งค่านี้เฉพาะบน response ของ launch ของตัวเองเท่านั้น จากนั้นเบราว์เซอร์จะเปิด management_url ของคุณพร้อม dee_wan_launch ใน query ดังนั้นหน้าของคุณต้องส่ง Referrer-Policy: no-referrer เอง และลบรหัสออกจาก address bar: แลกรหัสฝั่ง server แล้ว redirect ไปยัง URL เดิมโดยไม่มี dee_wan_launch
ขีดจำกัด ค่า
Request body 16384 ไบต์ เกินขีดจำกัดคือ limit_exceeded
Body ของการเขียนฟิลด์หรือข้อเสนอ 81920 ไบต์, 50 ฟิลด์, ค่ารวม 65536 ไบต์
Body ของ draft 1916384 ไบต์ draft หนึ่ง 1900000 ไบต์ 200 รายการต่อบุคคล
Body ของ media 33619968 ไบต์ ไฟล์หนึ่ง 33554432 ไบต์
Manifest body 32768 ไบต์
Idempotency key 128 อักขระ
หน้าของเนื้อหา 50 รายการ, ค่าเริ่มต้น 20
รายการของ delegation id ของรายการ 100 ตัวต่อการอ่านหนึ่งครั้ง
Request ต่อ installation 120 ต่อหน้าต่าง 60s, ใช้ installation เป็น key
อายุของ launch code 60s, ใช้ครั้งเดียว, เก็บแบบ hash
ช่วงซ้อนทับตอน rotate token ไม่เกิน 10 นาที และสิ้นสุดเมื่อมี request แรกที่ใช้ token ใหม่
Idempotency record 30 วัน หลังจากนั้น key เดิมคือคำสั่งใหม่
อายุของ delegation ไม่มีขีดจำกัดเวลา ดู Delegation
การเรียกใช้ 10 วินาทีสำหรับ POST การแลกรหัส และคำตอบรวมกัน รหัสหมดอายุพร้อมกัน
คำตอบของการเรียกใช้ 65536 ไบต์
ข้อมูลเข้าของการเรียกใช้ 32768 ไบต์ของค่าการดำเนินการหรือข้อเท็จจริงของนโยบาย
การเล่นซ้ำของการดำเนินการ 24 ชั่วโมงสำหรับ Idempotency-Key หนึ่งตัวของผู้เรียกหนึ่งคน
Capability สิ่งที่อนุญาต
content:read route สำหรับอ่านเนื้อหา ภายในโมเดลที่ได้รับ grant
workflow:read บล็อก workflow ในการอ่าน: state, revision และ transition ที่ข้ามขอบเขตการเผยแพร่ คำสั่งต้อง pin workflow.revision ดังนั้นปลั๊กอินที่ส่งคำสั่งต้องมีสิ่งนี้
workflow:publish คำสั่ง kind: publish
workflow:unpublish คำสั่ง kind: unpublish
workflow:locale คำสั่งที่ระบุ locale หนึ่งภาษาและเคลื่อนย้ายเฉพาะภาษานั้น และรายการ workflow.locales ตอนอ่าน หากไม่มี คำสั่งที่ระบุ locale จะได้ capability_refused คำสั่งที่ไม่มี locale จะเคลื่อนย้ายทั้ง translation group เสมอ
workflow:release เตรียมและอ่านเวอร์ชันอนาคตของการติดตั้งนี้ หรือส่งคำสั่งเผยแพร่และเลิกเผยแพร่ที่มีขีดจำกัด โดย commit แบบอะตอมมิกครั้งเดียวในฐานข้อมูลเว็บไซต์
content:write บันทึกฟิลด์ค่าของ item ในนามบุคคลที่มอบสิทธิ์ โดย pin กับเวอร์ชันปัจจุบันของ item
content:create สร้างรายการภายในสิทธิ์ปัจจุบันของบุคคลนั้น
content:delete ย้ายรายการไปถังขยะภายในสิทธิ์ปัจจุบันของบุคคลนั้น
draft:write อ่าน เขียน และทิ้ง working draft ของบุคคลที่มอบสิทธิ์เอง
media:write อัปโหลด media ในนามบุคคลที่มอบสิทธิ์ ซึ่งต้องมี media:upload
media:read อ่านไฟล์และ metadata ของสื่อที่มีอยู่
media:update แก้ metadata ของสื่อที่มีอยู่
media:delete ลบสื่อที่ไม่ใช้งานด้วยสิทธิ์ media:delete ปัจจุบัน และตรวจการอ้างอิงในประวัติที่เก็บไว้
content:restore กู้รายการจากถังขยะด้วยสิทธิ์ลบและสิทธิ์ภาษาปัจจุบันของผู้ใช้
comments:read อ่านความคิดเห็นผู้เข้าชมด้วยสิทธิ์ตรวจทานปัจจุบันและการมองเห็นโมเดลที่ได้รับสิทธิ์
comments:moderate จัดการความคิดเห็นผ่านการตรวจความยินยอมและความขัดแย้งของสถานะที่มีอยู่
redirects:read อ่าน redirect ด้วยสิทธิ์ site:settings ปัจจุบัน
redirects:write ตรวจและแก้ redirect ผ่านการตรวจรีวิชัน เมนูและ SEO และรูปแบบ redirect เดิม
mail:send ส่งเทมเพลตและผู้รับที่ผู้ดูแลอนุมัติ ด้วยสิทธิ์ site:plugins ปัจจุบันและค่าทดแทนที่ประกาศและจำกัดไว้
renderer:metadata บันทึกเมทาดาทาที่ประกาศสำหรับแต่ละภาษาเป็นเวอร์ชันฉบับร่างใหม่ โดยค่า SEO และการยืนยันเว็บไซต์จากระบบหลักมีลำดับความสำคัญสูงกว่า
content:propose เสนอค่าฟิลด์ให้ editor ตรวจ และอ่านข้อเสนอของ installation นี้
media:source ตอบ media_sources ของ manifest: ผู้ใช้เรียกดูภายในตัวเลือกสื่อและคัดลอกไฟล์ที่เลือกเข้าคลังของไซต์เอง หรือลิงก์ไฟล์สำหรับ source ที่ประกาศโหมด reference ไม่อ่านอะไรจาก core

grant (สิทธิ์ที่มอบให้) คือส่วนตัดกันของ capability เหล่านี้กับ list ของ id ของโมเดลเนื้อหาต่อไซต์ list โมเดลที่ว่างหมายถึงไม่มีสิทธิ์เข้าถึงเนื้อหา — ไม่ใช่ทุกโมเดล capability ที่อยู่นอกคำขอใน manifest ที่เก็บไว้ ไม่สามารถ grant ได้ ดังนั้นการอัปเดตปลั๊กอินไม่มีทางขยาย grant ที่มีอยู่ได้

pending, active, disabled, revoked มีเพียง active ที่เรียก plugin API ได้ revoked เป็นสถานะสุดท้าย: token ของมันจะไม่กลับมาใช้ได้อีก และการถอนการติดตั้งจะ revoke ก่อนสิ่งอื่นใด

ตอนติดตั้ง (kind: "activate") และตอน rotate token (kind: "rotate") core จะ POST $defs/activation_handoff เป็น JSON ไปยัง activation_url ของ manifest ซึ่งมี token ดิบ และ activation_code ที่ผู้ดูแลวางลงใน Dee Wan ปลั๊กอินเป็นผู้ออกรหัสนั้น หนึ่งรหัสต่อการติดตั้งหรือ การ rotate หนึ่งครั้ง และต้องตรวจมัน core ยอมรับเฉพาะรหัสที่ตรงกับ ^[A-Za-z0-9_-]{16,128}$ 2xx ใดก็ตามคือการยอมรับ token status อื่นใด, redirect หรือการไม่ตอบภายใน 5s คือการปฏิเสธ

token ยังใช้ไม่ได้ขณะที่ core รอคำตอบนั้น: installation ยังคงเป็น pending จนกว่า core จะได้รับ 2xx ของคุณ การเรียก /api/plugin/v1 จากภายใน handler จะได้คำตอบ plugin_unauthorized ให้เก็บ token ตอบ 2xx แล้วจึงเรียก /context การ rotate ทำงาน แบบเดียวกัน: token ใหม่ใช้ได้หลังจาก 2xx ของคุณเท่านั้น

บุคคลหนึ่งเปิดปลั๊กอินจาก Dee Wan ปลั๊กอินแลก launch code ที่ /launch/exchange และได้รับ delegation_id การแลกรหัสของการดำเนินการกับรายการก็คืนค่านี้เช่นกัน โดยผูกกับรายการนั้น ทุกคำสั่งต้องระบุหนึ่งตัว และการอ่านอาจระบุ หนึ่งตัว มันบอกว่าใครเป็นผู้ร้องขอ มันไม่เพิ่ม capability ใดเกินกว่า grant ของ installation

  • ไม่มีขีดจำกัดเวลา delegation ไม่หมดอายุตามเวลา ดังนั้นรายการตั้งเวลาจึงใช้มันได้ในอีกหลายวันหรือ หลายสัปดาห์ต่อมา
  • มันหยุดใช้งานได้ เมื่อบุคคลของมันถูกปิดใช้งานหรือไม่ได้เป็นสมาชิกของไซต์อีกต่อไป (requester_unauthorized) และหยุดถาวรเมื่อ installation ถูกถอนการติดตั้ง ซึ่งจะ revoke token ของมัน (plugin_unauthorized ในทุก request) ขณะที่ installation ถูกปิดใช้งานหรือไซต์ ไม่ active ทุก request ก็จะตอบ plugin_unauthorized เช่นกัน
  • อำนาจถูกอ่านซ้ำในทุก request คำสั่งจะถูกปฏิเสธด้วย requester_unauthorized เมื่อ บุคคลนั้นไม่สามารถทำ transition นั้นได้อีกต่อไป การอ่านจะตอบ transitions: [] แทน
  • Delegation แบบ content-action (มีการตั้ง instance_id) พูดแทนได้เฉพาะ translation group ของ รายการที่ถูก launch เท่านั้น: รายการนั้น คำแปลของมัน และรายการหลักของกลุ่ม รายการอื่นใด หรือรายการที่ถูก launch แต่ไม่มีอยู่แล้ว คือ requester_unauthorized
  • Delegation แบบ manage (instance_id เป็น null) พูดแทนรายการของโมเดลใน model_ids ของมัน: โมเดลใน grant ที่บุคคลนั้นอ่านได้ตอน launch ทุกรายการหรือเฉพาะรายการของตัวเอง
  • โมเดลถูกตรวจซ้ำในทุก request รายการที่โมเดลไม่อยู่ใน model_ids หรือที่บุคคลนั้นอ่านไม่ได้แล้ว คือ requester_unauthorized /delegations/:delegationId แสดงสิ่งที่ delegation เข้าถึงได้ในตอนนี้
  • รายการของตัวเองถูกตรวจทีละรายการ ภายใต้ read:own หรือ update:own โมเดลจะกลับมาเป็น entries: "checked" /delegations/:delegationId/entries บอกว่าในรายการที่คุณถืออยู่ บุคคลนั้นอ่านรายการใดได้ในตอนนี้ และทำอะไรกับรายการนั้นได้บ้าง อย่าแสดงสิ่งที่คำตอบตัดออก
  • credential จำกัดมัน delegation ที่เปิดโดย credential ของ management มีเฉพาะ capability ของ installation ที่ scopes ของ credential นั้นอนุญาต คำสั่งที่อยู่นอกนั้นคือ capability_refused
  • grant ของโมเดลถูกตรวจซ้ำในทุก request รายการที่อยู่นอก grant ปัจจุบันคือ content_not_found ไม่ว่า delegation จะเป็นแบบใด
  • ค่า action สาธารณะถูกส่งกลับตามที่เปิดไว้ คำขอที่ได้รับมอบสิทธิ์ครั้งต่อไปจะตรวจการทำงานที่ต้องใช้ตามคำประกาศและสิทธิ์ปัจจุบันของผู้ใช้ การเปิดไว้ก่อนหน้าไม่ทำให้ใช้สิทธิ์ที่ถูกเพิกถอนแล้วได้

การกำหนดสิทธิ์การทำงานของปลั๊กอินผ่านบทบาท

หัวข้อที่มีชื่อว่า “การกำหนดสิทธิ์การทำงานของปลั๊กอินผ่านบทบาท”

ประกาศการทำงานแยกกันด้วย role_actions: [{ id, label }] โดย id ใช้รูปแบบตัวระบุภายในที่คงที่เช่นเดียวกับ operation ผู้ดูแลกำหนดสิทธิ์ที่ การตั้งค่า → บทบาท และบทบาทเก็บสิทธิ์เป็น plugin_actions: ["<manifest-id>/<action-id>"] บทบาทที่เข้าถึงเนื้อหาเหมือนกันจึงมีสิทธิ์ใช้การทำงานของปลั๊กอินต่างกันได้

ระบุ requires_actions: ["<action-id>"] ในการทำงานกับรายการ มุมมองผู้ดูแล หรือ operation สำหรับมุมมองจัดการที่มีชื่อ ให้ใช้ manage_view_action_requirements: { "<view-id>": ["<action-id>"] } การทำงานและมุมมองทั้งหมดต้องประกาศใน manifest เดียวกัน ตัวระบุที่ซ้ำ ไม่รู้จัก หรือเป็นของปลั๊กอินอื่นจะถูกปฏิเสธ ข้อกำหนดนี้ไม่เปลี่ยนความสามารถที่อนุญาตตอนติดตั้ง สิทธิ์เข้าถึงโมเดล ข้อจำกัดฟิลด์และภาษา หรือสิทธิ์ในขั้นตอนงาน

ระบบตรวจสิทธิ์เมื่อแสดง เปิด และเรียกใช้การทำงาน รวมถึงคำขอที่ได้รับมอบสิทธิ์ เมื่อลบสิทธิ์หรือคำประกาศการทำงาน เซสชันเดิมจะใช้การทำงานนั้นไม่ได้ ผู้ดูแลระดับสูงหรือผู้ดูแลที่มีสิทธิ์ระบบ * กำหนดสิทธิ์ครั้งแรกได้ แต่ site:plugins เพียงอย่างเดียวไม่ให้สิทธิ์ใช้การทำงาน การกำหนดสิทธิ์เดิมของปลั๊กอินที่ถอนการติดตั้งแล้วคงไว้ได้โดยไม่ต้องแก้ไข แต่ไม่มีผลขณะที่ไม่มีคำประกาศรองรับ

ข้อมูลรับรองของ Management API ต้องมี scope plugin_action:<manifest-id>/<action-id> ที่เลือกไว้อย่างชัดเจนด้วย plugins:use เพียงอย่างเดียวไม่เพียงพอ ตอนออกข้อมูลรับรอง ระบบตรวจคำประกาศของปลั๊กอินที่ติดตั้งและสิทธิ์ของผู้สร้าง ตอนใช้งาน ระบบใช้เฉพาะสิทธิ์ที่ทั้ง scope และสิทธิ์ปัจจุบันของผู้สร้างอนุญาต สิทธิ์ระบบ * ของผู้สร้างไม่ขยายข้อมูลรับรองเกินการทำงานที่เลือกไว้

manifest ประกาศ operations (ไม่เกิน 8) และ policies (ไม่เกิน 8) ได้ core เรียกใช้สิ่งเหล่านี้บน ปลั๊กอิน core วาดทุกฟอร์มและทุกผลลัพธ์ด้วยคอมโพเนนต์ของตัวเอง ไม่มีสิ่งใดที่ปลั๊กอินส่งมาเป็น markup หรือโค้ด

  • การดำเนินการ มี input แบบปิดไม่เกิน 8 ช่อง — text (มี max_length), number (min, max, integer), boolean, select (options แบบปิด) — และรายการ result ของคอมโพเนนต์ CMS: text, rows (คีย์/ค่า), links (https เท่านั้น) และ json (ให้ดาวน์โหลด) scope: "site" เรียกใช้จาก การตั้งค่า → ปลั๊กอิน และต้องมี site:plugins scope: "entry" เรียกใช้กับรายการเดียวของ โมเดลที่ให้สิทธิ์ซึ่งผู้ใช้อ่านได้ ถ้ามี destructive: true ผู้ใช้ต้องแก้ไขรายการนั้นได้ด้วยและยืนยันก่อน ต้องระบุ audience การดำเนินการ interactive แสดงและเรียกใช้ได้ในหน้าผู้ดูแล, API การจัดการ, CLI และ MCP การดำเนินการ automation ไม่แสดงในที่เหล่านั้น และเส้นทางเรียกใช้เหล่านั้นปฏิเสธมัน มีเพียง กระบวนการของ core ที่ระบุชื่อ (การส่งต่อจากตัววางแผนเนื้อหาไปยัง Scheduling) ที่เรียกใช้ได้ ผ่าน transport เดียวกัน อำนาจปัจจุบัน สิทธิ์ idempotency และการบันทึกตรวจสอบเดียวกัน
  • นโยบาย ประกาศ kind หนึ่งชนิด: save (การบันทึกเนื้อหาทุกครั้ง รวมถึงการบันทึกที่เปลี่ยนเฉพาะลิงก์ สื่อ หรือการตั้งค่าการค้นหา) restore (การกู้คืนเวอร์ชันเก่า หรือรายการที่ออกจากถังขยะ) delete (รายการ หรือภาษาที่ถูกย้ายลงถังขยะ หรือถูกลบถาวร) publish, unpublish และ transition (การย้ายเวิร์กโฟลว์ทุกครั้ง รวมถึงการย้ายตามเวลาและจากปลั๊กอิน) ข้อมูลเข้าที่ผูกไว้คือ $defs/policy_facts: ชนิด id และ slug ของโมเดล id ของรายการ (null สำหรับรายการใหม่) ภาษา changed_fields, values และในกรณีที่ใช้ได้ links, media, seo, action และ transition สำหรับ save และ restore จะมีส่วนที่เปลี่ยนของตัวที่จะบันทึก สำหรับชนิดอื่น คือทั้งเวอร์ชันที่เกี่ยวข้อง ลิงก์ที่ทุกภาษาใช้ร่วมกันและสื่อมากับภาษาหลักเท่านั้น ไม่มีข้อมูลรับรองและไม่มี เนื้อหาอื่น นโยบายตอบ $defs/policy_reply: allow, warning พร้อมข้อความ, refuse พร้อมรหัสและข้อความ หรือ — เฉพาะ save และ restore — transform พร้อม values, links, media หรือ seo ที่ใช้แทน fields ระบุคีย์ของช่อง
  • การดำเนินการอาจเป็นคอลัมน์ในรายการเนื้อหาหรือการทำงานกับแถวก็ได้ คอลัมน์คือ scope: "page" กับ audience: "column" ชนิด cell (text, badge หรือ link) result: ["cells"] ไม่มี input และไม่ใช่ destructive สำหรับโมเดลหนึ่งในสิทธิ์ มันได้รับ $defs/column_request ได้แก่โมเดลและรหัสของแถวไม่เกิน 50 แถวที่ คนนั้นอ่านได้ ไม่ใช่ค่า และตอบด้วยบล็อก cells หนึ่งบล็อก มีไม่เกินหนึ่งเซลล์ต่อแถวที่ถูกระบุ แต่ละเซลล์ { id, text, tone?, url? } (tone สำหรับป้ายสี url แบบ https สำหรับลิงก์ ข้อความไม่เกิน 80) มันอ่านสิ่งที่ต้องใช้ ผ่านสิทธิ์ของตัวเอง คอลัมน์ไม่สร้างการมอบสิทธิ์ และไม่ถูกแสดงรายการหรือรันเป็นการดำเนินการทั่วไป การทำงานกับแถวคือการ ดำเนินการ entry แบบ interactive ที่มี list_action: true core รันมันหนึ่งครั้งต่อแต่ละแถวจากไม่เกิน 25 แถวที่คนเลือก โดยตรวจสิทธิ์ปัจจุบันของคนนั้นต่อแถว
  • requires แสดง capability ของการติดตั้งจาก requested_capabilities ระหว่างที่สิทธิ์ยังขาดอย่างใด อย่างหนึ่ง รายการนั้นใช้ไม่ได้ การรับ manifest ใหม่ไม่เคยขยายสิทธิ์ รายการใหม่ที่ต้องการมากกว่าจึงใช้ ไม่ได้จนกว่าผู้ดูแลจะให้สิทธิ์

การเรียกใช้หนึ่งครั้งตามลำดับ:

  1. core บันทึกการเรียกใช้ — การติดตั้ง ไซต์ ผู้เรียก id ที่ประกาศ โมเดลและรายการ (ถ้ามี) digest ของ ข้อมูลเข้า เวลาหมดอายุ — และ SHA-256 ของรหัส dwi_ แบบใช้ครั้งเดียว
  2. core POST $defs/invocation_envelope ไปยัง endpoint ที่ประกาศ: เวอร์ชันโปรโตคอล id การติดตั้ง id การเรียกใช้ รหัส และเวลาหมดอายุ ไม่มีเนื้อหาและไม่มีข้อมูลรับรอง การ redirect ถูกปฏิเสธ
  3. ปลั๊กอินส่งรหัสไปที่ POST /api/plugin/v1/invocations/exchange พร้อม bearer token ของตัวเอง แล้ว ได้ข้อมูลเข้าที่ผูกไว้ ($defs/invocation_binding) core ใช้รหัสได้ครั้งเดียว
  4. ปลั๊กอินตอบ POST ของ core ด้วย HTTP 200 และ $defs/operation_reply หรือ $defs/policy_reply

core รับคำตอบเฉพาะเมื่อการติดตั้งนี้แลกรหัสของการเรียกใช้นี้และยังครอบคลุมอยู่: ใช้งานอยู่ เวอร์ชันและ การประกาศเดิม capability ที่ต้องใช้และโมเดลยังได้รับสิทธิ์ครบ นอกนั้น core จัดประเภทและเก็บเพียง digest ไม่ใช่เนื้อความ ไว้ในบันทึกการตรวจสอบ:

ความล้มเหลว เมื่อ
plugin_timeout ไม่มีคำตอบครบภายใน 10 วินาที
plugin_unreachable เชื่อมต่อไม่ได้ redirect หรือ 5xx
plugin_response_invalid สถานะอื่น ไม่ตรงสคีมาคำตอบ ใช้คอมโพเนนต์ผลลัพธ์ที่ไม่ได้ประกาศ ใหญ่เกินไป หรือตอบโดยไม่แลกรหัส
plugin_refused การดำเนินการตอบ status: "refused" พร้อมรหัสและข้อความของตัวเอง
plugin_unavailable การติดตั้งถูกปิด ถอน ลดสิทธิ์ หรือเปลี่ยนระหว่างการเรียกใช้

มีเท่านี้: plugin_timeout, plugin_unreachable, plugin_response_invalid, plugin_refused, plugin_unavailable อย่าตอบก่อนแลกรหัส และอย่าแคชข้อมูลที่ผูกไว้ การเรียกใช้แต่ละครั้งมีรหัสของตัวเอง

  • ระบบหลักถามหลังการตรวจของตัวเอง (ฟิลด์ กฎ สิทธิ์ เวิร์กโฟลว์ การเผยแพร่) และก่อนบันทึก นโยบายไม่แทนที่การตรวจเหล่านั้น และไม่ย้ายเวิร์กโฟลว์เลย
  • ระบบถามการติดตั้งที่ใช้งานอยู่ซึ่งสิทธิ์ครอบคลุมโมเดลของรายการและนโยบายพร้อมใช้งาน ถามพร้อมกัน ไม่เกิน 8 รายการในเวลาเดียวกัน การบันทึกหรือการกู้คืนถามหนึ่งครั้งต่อภาษาที่ค่า ลิงก์ สื่อ หรือการตั้งค่าการค้นหาเปลี่ยน การเผยแพร่ การยกเลิกการเผยแพร่ การลบ หรือการย้าย ถามหนึ่งครั้งต่อภาษาที่ได้รับผลกระทบ การกู้คืนถามนโยบาย save และ restore
  • transform แทนที่ส่วนของรายการและภาษาที่ถูกถามตามคีย์ฟิลด์ และไม่มีอย่างอื่น: ไม่ได้ระบุรายการ จึงเข้าถึง รายการอื่นไม่ได้ values และ links ต่อภาษาลงในภาษานั้น ลิงก์ที่ทุกภาษาใช้ร่วมกันและ media รับจากภาษาหลัก เท่านั้น seo แทนที่การตั้งค่าการค้นหาของภาษานั้น (รูปแบบเดียวกับตัวแก้ไข ภาพเป็น { media_id, alt }) รายการ แทนที่ทั้งรายการ การกู้คืนรับเฉพาะ values และ seo จากนั้นระบบหลักรันการเขียนทั้งหมดอีกครั้งโดยใส่ส่วนของคุณ ผ่านทุกกฎที่ข้อมูลจากผู้ใช้ต้องผ่าน (กฎฟิลด์ rich text ความสัมพันธ์ สื่อ ความไม่ซ้ำ สิทธิ์) ส่วนที่ระบบปฏิเสธจะทำให้ การเขียนถูกปฏิเสธเป็น plugin_policy_transform_invalid พร้อมสาเหตุ สองนโยบายที่เปลี่ยนส่วนเดียวกันต่างกันจะถูก ปฏิเสธเป็น plugin_policy_transform_conflict นโยบายของคุณไม่ถูกถามอีกในรอบที่สอง ผลลัพธ์แสดง outcome: "transformed" พร้อมฟิลด์ที่คุณเปลี่ยนและ message ที่ไม่บังคับ และเวอร์ชันที่เก็บบันทึกไว้ใน meta.policy_transforms transform ที่ไม่เปลี่ยนอะไรคือการอนุญาต transform สำหรับชนิดอื่นเป็นคำตอบที่ระบบหลักไม่รับ
  • refuse ปฏิเสธการเขียนเสมอ ตัวแก้ไขจะระบุชื่อปลั๊กอินและนโยบายของคุณ และแสดงข้อความของคุณที่ฟิลด์ที่คุณระบุ warning ให้การเขียนผ่านและแสดงข้อความ ไม่มีใครต้องส่งซ้ำ
  • หากหมดเวลา ระบบล่ม หรือคำตอบที่ระบบหลักไม่รับ จะเป็นไปตามโหมดที่ผู้ดูแลเลือกให้นโยบายใน การตั้งค่า → ปลั๊กอิน: warn (ค่าเริ่มต้น) ให้การเขียนผ่านพร้อมคำเตือน required ปฏิเสธการเขียน
  • caller.via คือ person, management_credential, planner เมื่อตัววางแผนเนื้อหาทำงานแทนเจ้าของแผน หรือ plugin เมื่อกำลังตรวจการเขียนที่ปลั๊กอินอื่นทำแทนบุคคล การนำเข้า สเปรดชีต และการกู้คืน ถามในนามของบุคคลที่สั่งงาน
  • การเขียนจะบันทึกก็ต่อเมื่อสิ่งที่คุณเห็นยังเป็นปัจจุบัน การเปลี่ยนแปลงระหว่างนั้นคือข้อขัดแย้งของเวอร์ชันตามปกติ และคำตอบของคุณจะไม่ถูกใช้กับเนื้อหาอื่น

manifest ประกาศ hooks ได้ไม่เกินหนึ่งรายการต่อชนิด คือ upload_metadata และ comment_moderation แต่ละรายการมี id, label, endpoint HTTPS ใน origin เดียวกัน และ requires (capability ที่อยู่ใน requested_capabilities) hook ปิดอยู่จนกว่าผู้ดูแลจะเปิดให้ installation ใน การตั้งค่า → ปลั๊กอิน Core ถาม hook ที่เปิดอยู่ ก่อน การดำเนินการ ไม่ใช่หลังจากนั้น: upload_metadata ก่อนเก็บไฟล์ใหม่ในคลัง และ comment_moderation ก่อนเก็บความคิดเห็นของผู้เยี่ยมชม

  • hook ถูกเรียกผ่านระบบขนส่ง invocation เดียวกัน โดย binding มี kind: "hook", caller: null และไม่มี delegation Core บอก hook ว่าถูกถามอะไร และไม่เคยบอกว่าใครถาม: ไม่มีบุคคล อีเมล credential ไบต์ของไฟล์ หรือรายการอื่น
  • upload_metadata: ข้อมูลนำเข้าคือ $defs/hook_upload_facts (ชื่อไฟล์ ประเภท MIME ขนาด โฟลเดอร์ และข้อความแทนรูป; ความกว้างและความสูงเป็น null เพราะยังไม่ได้วัด) ตอบด้วย $defs/hook_upload_reply: accept จะใส่ normalized ด้วยก็ได้ (filename ใหม่ที่คงนามสกุลเดิม และข้อความแทนรูปเฉพาะภาษาที่การอัปโหลดระบุไว้แล้ว) หรือ refuse พร้อมรหัสและข้อความที่ผู้อัปโหลดอ่านได้ (422 upload_hook_refused)
  • comment_moderation: ข้อมูลนำเข้าคือ $defs/hook_comment_facts (รายการเนื้อหา ID ของความคิดเห็น ความคิดเห็นแม่ ชื่อผู้เขียน เว็บไซต์ และข้อความ; ไม่มีอีเมล) ตอบด้วย $defs/hook_comment_reply: approve, hold (ยังรอการตรวจ) หรือ reject (เก็บเป็นสแปม หรือเป็น refused เมื่อมี as: "refused")
  • hook ที่ตอบไม่ได้จะไม่อนุมัติเด็ดขาด การหมดเวลา (5 วินาที) ระบบล่ม เนื้อหาที่ไม่ใช่คำตอบที่กำหนด หรือ installation ที่ถูกปิดหรือเปลี่ยนระหว่างทำงาน จะปฏิเสธการอัปโหลด (503 upload_hook_unavailable ไม่บันทึกอะไร) หรือปล่อยความคิดเห็นให้รอการตรวจ
  • retry identity คือ digest ของคำถาม: การอัปโหลดซ้ำหรือความคิดเห็นที่ส่งซ้ำจะได้คำตอบแรกที่ ยอมรับแล้ว กลับมานาน 24 ชั่วโมงแทนการถามใหม่ คำถามที่ล้มเหลวจะไม่ถูกเล่นซ้ำ คำถามเดิมครั้งถัดไปจะถามใหม่

manifest อาจประกาศ media_sources (สูงสุด 4) และขอ media:source แต่ละ source คือคลังที่ผู้ใช้เรียกดูได้ภายใน ตัวเลือกสื่อ ข้างคลังของไซต์เอง และผ่าน management API แต่ละ source ประกาศ modes: copy (core คัดลอกไฟล์ที่เลือกเข้าไซต์ เนื้อหาที่เผยแพร่จึงไม่ขึ้นกับ source), reference (core เก็บเฉพาะ URL การส่งมอบและตัวตนของไฟล์ และ source เก็บไบต์) หรือทั้งสอง ผู้ใช้หรือผู้เรียก API ระบุโหมดทุกครั้งที่เพิ่ม core ไม่เดาจาก URL หรือชื่อ

  • source ถูกเรียกผ่าน transport การเรียกใช้เดียวกัน โดยมี kind: "media_source" ในข้อมูลที่ผูก และไม่มี delegation ข้อมูลเข้าที่ผูกไว้คือ $defs/media_source_request: browse พร้อม query (ว่างสำหรับหน้าแรก) cursor (next_cursor ก่อนหน้า หรือ null) และ limit (สูงสุด 24) หรือ item พร้อม item_id ที่ผู้ใช้เลือก
  • ตอบ browse ด้วย $defs/media_source_page_reply และ item ด้วย $defs/media_source_item_reply หรือ status: "refused" พร้อมรหัสและข้อความ แต่ละ item มีชื่อเรื่อง ชื่อไฟล์ ชนิด MIME ขนาดและมิติ (ไม่บังคับ) thumbnail_url, download_url (จำเป็นสำหรับ copy), delivery_url (จำเป็นสำหรับ reference) และ attribution: source_name, license และ author, author_url, license_url และ source_url ที่ไม่บังคับ description ของรูปภาพ (สูงสุด 125 ตัวอักษร) จะกลายเป็น alt text ในภาษาเริ่มต้นของไซต์
  • thumbnail_url และ download_url ต้องอยู่บน origin เดียวของ manifest ส่วน delivery_url ต้องอยู่บนหนึ่งใน delivery_origins ของ source (สูงสุด 4 origin เปล่า จำเป็นเมื่อมี reference และต้องไม่มีเมื่อไม่มี origin ของ manifest อยู่ในนั้นได้) คำตอบที่ระบุ origin อื่นคือ plugin_response_invalid ตัวเลือกโหลด thumbnail จาก origin ของคุณโดยตรง โดยไม่มี referrer และไม่มีข้อมูล รับรองของ Dee Wan ลิงก์ attribution แสดงไว้แต่ไม่ถูกดึง
  • เมื่อผู้ใช้เลือก item core ถาม item อีกครั้ง แล้ว GET download_url โดยไม่มีข้อมูลรับรองและไม่มี redirect ภายใน 20 วินาทีและขีดจำกัดไฟล์สื่อ ไบต์ผ่านการตรวจเดียวกับการอัปโหลด: ชนิด รูปแบบ ขีดจำกัดขนาดของไซต์ และการตรวจไฟล์ซ้ำ สื่อใหม่คง attribution และ id ของปลั๊กอิน source และ item ไว้ ลงนาม download_url เองหากไฟล์ของคุณไม่เป็นสาธารณะ
  • ในโหมด reference core ไม่ดึงอะไร แถวสื่อเก็บ delivery_url ของคุณเป็นที่อยู่ เก็บ id ของปลั๊กอิน source และ item ของคุณเป็นตัวตน และเมทาดาทาที่คุณประกาศ item เดิมที่เพิ่มสองครั้งทำตาม on_duplicate หน้าที่เผยแพร่ สำเนาไซต์ และไซต์ที่สร้าง ใช้ delivery_url ตามที่เป็น จึงต้องเปิดให้บริการตราบที่ไซต์ยังใช้ core ไม่ proxy ไม่แทนที่ไบต์ และไม่เรียกคุณเมื่อ ไฟล์ถูกลบจากคลัง โหมดที่ source ไม่ได้ประกาศถูกปฏิเสธด้วย media_source_mode_unsupported
  • การเรียกดูไม่เปลี่ยนอะไร จึงไม่ถูกบันทึกตรวจสอบ สำเนาคือการอัปโหลดสื่อปกติโดยผู้ที่เลือก
  • ผู้เรียกต้องมี media:upload การติดตั้งที่ถูกปิด สิทธิ์ที่ถูกลดจนไม่มี media:source หรือการประกาศที่เปลี่ยน ทำให้ source ใช้ไม่ได้ทันที

installation นี้คืออะไร id ของ installation, ไซต์ที่มันผูกอยู่, เวอร์ชัน protocol ที่ยอมรับ และ grant ให้อ่านตอนเริ่มต้นเพื่อรู้ว่า installation นี้ขออะไรได้บ้าง มันไม่ระบุผู้ใช้, ไม่มี hash ของ token, ไม่มีฐานข้อมูล และไม่มีโครงสร้างพื้นฐานใด

  • ผู้เรียก: ปลั๊กอิน พร้อม token ของ installation
  • ต้องการ: ไม่มี
  • ไม่มี query ไม่มี body
  • คำตอบ: { "data": <context> } — $defs/context

Outcome:

  • plugin_unauthorized
  • rate_limited
  • temporary_failure

installation นี้ทำอะไรได้บ้าง

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/context" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"data": {
"installation_id": "plg_0000000000000000000000000000d1ee",
"site": {
"id": "cdocssite00000000000000001",
"name": "docs-site",
"default_lang": "en"
},
"protocol_version": 1,
"capabilities": [
"content:read",
"workflow:read",
"workflow:publish",
"workflow:unpublish"
],
"model_ids": [
"cdocsmodel0000000000000001"
]
}
}

token ที่ไม่รู้จัก ถูก disable หรือถูก revoke

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/context" \
-H "Authorization: Bearer dwp_UnknownTokenNotRealUnknownTokenNotRealUnkno"
{
"outcome": {
"code": "plugin_unauthorized",
"message": "The installation token is missing, unknown, disabled or revoked.",
"retry": false
}
}

คำตอบเดียวสำหรับทั้งสี่กรณี ไม่มีสิ่งใดบอกผู้เรียกว่าเป็นกรณีใด

ปลั๊กอินระบุไซต์ไม่ได้

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/context" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "X-Site-Id: cotherdocsite00000000001"
{
"outcome": {
"code": "invalid_request",
"message": "A plugin request cannot name a site.",
"retry": false
}
}

ไซต์มาจาก token การส่ง header นี้มาเลยจะถูกปฏิเสธ แม้จะเป็นค่าที่ถูกต้องก็ตาม

route ของปลั๊กอินที่ไม่รู้จัก

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/schedules" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"outcome": {
"code": "content_not_found",
"message": "No such plugin route.",
"retry": false
}
}

Protocol v1 มี route ข้างต้นและไม่มีอะไรอื่น 404 ตรงนี้ไม่ใช่ความล้มเหลวชั่วคราว

แสดงรายการเนื้อหาในโมเดลที่ได้รับ grant หนึ่งโมเดล instance หลักหนึ่งหน้าในโมเดลเดียว model_id เป็นค่าบังคับ: ไม่มีการอ่านแบบ “ทุกโมเดล” ชุด filter เป็นแบบปิด — query parameter อื่นใดคือ invalid_request ไม่ใช่ถูกเพิกเฉย — และหน้านี้เป็นสรุปแบบแคบ ไม่ใช่ snapshot ของเนื้อหา มีเพียง primary ของ translation group ที่ปรากฏ group คือหน่วยที่ transition เคลื่อนย้าย

  • ผู้เรียก: ปลั๊กอิน, content:read
  • ต้องการ: content:read
  • model_id (บังคับ) — id ของโมเดลจาก grant
  • published — true หรือ false
  • after — instance_id ตัวสุดท้ายของหน้าก่อนหน้า
  • limit — 1..50, ค่าเริ่มต้น 20
  • คำตอบ: { "data": [<summary>], "next": <instance_id|null> } — $defs/content_page

Outcome:

  • capability_refused — ไม่มี content:read หรือโมเดลอยู่นอก grant
  • invalid_request — ไม่มี model_id, filter ที่ไม่รู้จัก, limit อยู่นอกช่วง
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

หนึ่งหน้าของหนึ่งโมเดล

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/content?model_id=cdocsmodel0000000000000001&published=false&limit=1" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"data": [
{
"instance_id": "cdocsinstance00000000000001",
"primary_instance_id": "cdocsinstance00000000000001",
"translation_group_id": "cdocsgroup0000000000000001",
"model_id": "cdocsmodel0000000000000001",
"model_slug": "article",
"lang": "en",
"label": "Hello",
"slug": "hello",
"current_version_id": "cdocsversion00000000000001",
"published_version_id": null
}
],
"next": null
}

filter ที่อยู่นอก model_id, published, after, limit คือ invalid_request ไม่ใช่ถูกเพิกเฉย

ชุด filter เป็นแบบปิด

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/content?model_id=cdocsmodel0000000000000001&sort=title" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"outcome": {
"code": "invalid_request",
"message": "model_id is required; filters are model_id, published, after, limit.",
"retry": false
}
}

อ่านรายการเดียว พร้อมการดำเนินการที่ผู้ใช้ทำได้ในตอนนี้ id, label (ชื่อเรื่องของรายการในภาษาของรายการนั้น หรือหมายเหตุของ version ปัจจุบันหากไม่มี), slug, id ของ version ปัจจุบันและ version ที่เผยแพร่ และ — เมื่อมี workflow:read — state ของ workflow, revision ของมัน และ transition ที่ข้ามขอบเขตการเผยแพร่ transition ถูกจำกัดให้แคบลงตาม capability ที่ installation นี้ถืออยู่ ส่ง delegation_id มา แล้วมันจะถูกจำกัดให้แคบลงอีกตามสิ่งที่บุคคลนั้นทำได้ ในตอนนี้ โดยอ่านซ้ำในทุก request บุคคลที่ถูกปิดใช้งานหรือไม่ได้เป็นสมาชิกของไซต์อีกต่อไปจะตอบ requester_unauthorized บุคคลที่ยังเป็นสมาชิกแต่ไม่สามารถอัปเดตรายการนี้ได้อีกต่อไปจะตอบ 200 พร้อม transitions: [] (คำสั่งจะยังคงถูกปฏิเสธด้วย requester_unauthorized) delegation แบบ content-action ถูกยอมรับเฉพาะกับรายการใน translation group ที่มันถูก launch delegation แบบ manage ถูกยอมรับกับรายการใดก็ได้ของโมเดลที่มันถูก launch ด้วย ทุก delegation ยังถูกปฏิเสธสำหรับรายการที่โมเดลไม่อยู่ใน model_ids ของมัน หรือที่บุคคลของมันอ่านไม่ได้แล้ว หากไม่มี workflow:read จะไม่มีบล็อก workflow delegation ที่ถูกต้องเพิ่ม snapshot saved ซึ่งจำกัดตามสิทธิ์ปัจจุบัน workflow.revision คือสิ่งที่ทุกคำสั่งต้อง pin ดังนั้นปลั๊กอินที่ส่งคำสั่งต้องมี workflow:read รายการที่ไม่เคยเคลื่อนผ่าน workflow ของมันเลยมี revision 0 รายการที่อยู่นอกไซต์หรือนอก grant ของโมเดลคือ content_not_found ซึ่งเป็นคำตอบเดียวกับ id ข้ามไซต์ด้วย

  • ผู้เรียก: ปลั๊กอิน, content:read
  • ต้องการ: content:read และ workflow:read สำหรับบล็อก workflow
  • delegation_id — launch delegation ที่ถูกใช้แล้ว หรือ delegation จากข้อมูลผูกของการดำเนินการกับรายการ ไม่รับ query parameter อื่น
  • คำตอบ: { "data": <summary> } — $defs/content_summary

Outcome:

  • capability_refused
  • content_not_found
  • requester_unauthorized — delegation ที่ระบุไม่เป็นที่รู้จัก เป็นของ installation อื่น ผูกกับ translation group อื่นหรือกับรายการที่ไม่มีอยู่แล้ว หรือบุคคลของมันถูกปิดใช้งานหรือไม่ได้เป็นสมาชิกของไซต์อีกต่อไป หรือโมเดลของรายการไม่อยู่ใน model_ids ของ delegation หรือบุคคลของมันอ่านรายการนั้นไม่ได้แล้ว
  • invalid_request
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

หนึ่งรายการ พร้อม transition ที่ผู้ร้องขอทำได้ในตอนนี้

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001?delegation_id=pld_0000000000000000000000000000d1ee" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"data": {
"instance_id": "cdocsinstance00000000000001",
"primary_instance_id": "cdocsinstance00000000000001",
"translation_group_id": "cdocsgroup0000000000000001",
"model_id": "cdocsmodel0000000000000001",
"model_slug": "article",
"lang": "en",
"label": "Hello",
"slug": "hello",
"current_version_id": "cdocsversion00000000000001",
"published_version_id": null,
"workflow": {
"state": "draft",
"revision": 0,
"transitions": [
{
"to": "published",
"kind": "publish"
}
]
}
}
}

ให้ pin สิ่งที่คำตอบนี้ให้มา: current_version_id สำหรับการเผยแพร่, published_version_id สำหรับการยกเลิกการเผยแพร่ และ workflow.revision สำหรับทั้งสองแบบ

เผยแพร่หรือยกเลิกการเผยแพร่แบบมีเงื่อนไข ใช้เปลี่ยนสถานะการเผยแพร่เท่านั้น มันมีเงื่อนไขสี่ทางพร้อมกัน ซึ่งทั้งหมดถูกตรวจภายใน lifecycle mutation แบบ atomic เดียวกัน: version ที่ถูก pin ต้องยังเป็นตัวที่ transition จะเคลื่อนย้าย, revision ของ workflow ต้องยังเป็นตัวที่ใช้ตอนสร้างรายการตั้งเวลา, targetState ต้องเป็น transition ที่ใช้ได้จาก state ปัจจุบัน และความหมายด้านการเผยแพร่ของ transition นั้นต้องตรงกับ kind delegation ต้องครอบคลุมรายการนั้น (ดู Delegation) และอำนาจของบุคคลที่ delegate จะถูกอ่านซ้ำก่อนการเขียน draft ที่ใหม่กว่าจะไม่ถูกเผยแพร่อย่างเงียบ ๆ เลย workflow ที่เปลี่ยนไปจะไม่ถูกเดินผ่านอย่างเงียบ ๆ เลย การเผยแพร่มีผลกับทั้ง translation group ซึ่งเป็นเหตุผลที่ผลลัพธ์ระบุ instance หลัก เมื่อมี workflow:locale body ที่ระบุ locale จะเคลื่อนย้ายเฉพาะภาษานั้น: targetState ถูกตรวจกับ state ของภาษานั้นเองใน workflow.locales การยกเลิกการเผยแพร่จะ pin เวอร์ชันที่เผยแพร่ของภาษานั้นเอง และผลลัพธ์ระบุ locale ที่ถูกเคลื่อนย้าย transition ที่เผยแพร่ยังรายงาน redirect: การ redirect อัตโนมัติที่ lifecycle call เดียวกันสร้างให้ที่อยู่ที่เปลี่ยน หรือเหตุผลที่ไม่ได้สร้าง การส่งซ้ำตรงตัวได้ผลลัพธ์แรกทั้งหมด รวม locale และ redirect core เขียน redirect หลัง transition commit จนกว่าจะบันทึกผลของมัน คำสั่งและการส่งซ้ำใด ๆ จะตอบ temporary_failure พร้อมข้อความว่า transition ถูกนำไปใช้แล้ว ลองใหม่ด้วย key และ body เดิม: แล้วการส่งซ้ำจะได้ผลลัพธ์ทั้งหมด

การเผยแพร่ปกติตรึงฉบับร่างปัจจุบัน ส่วนการเผยแพร่เวอร์ชันอนาคตต้องระบุ future_draft_id คู่กับ expected_future_revision และตรึงเวอร์ชันที่บันทึกไว้ของฉบับนั้น โดยไม่เปลี่ยนฉบับร่างปกติ การเผยแพร่ซ้ำจากสถานะที่เผยแพร่แล้วทำได้เฉพาะคำสั่งเวอร์ชันอนาคตที่มีสิทธิ์ปัจจุบันเท่านั้น

  • ผู้เรียก: ปลั๊กอิน, workflow:publish หรือ workflow:unpublish
  • ต้องการ: workflow:publish สำหรับ kind: publish, workflow:unpublish สำหรับ kind: unpublish
  • Idempotency-Key (บังคับ) — อักขระ 1 ถึง 128 ตัวในช่วง \x21–\x7e (ASCII ที่พิมพ์ได้ ไม่มีช่องว่าง) ให้สร้างจากรายการตั้งเวลา ไม่ใช่จากนาฬิกา: replay ที่ตรงกันทุกประการจะคืนผลลัพธ์แรก และ key เดิมกับอินพุตที่ต่างกันคือ idempotency_key_reused core เก็บ record ไว้ 30 วัน หลังจากนั้น key เดิมจะถือเป็นคำสั่งใหม่
  • Body — $defs/publish_command หรือ $defs/unpublish_command shape เป็นแบบ ปิด: key ที่ไม่รู้จัก หรือ pin ของอีก kind หนึ่ง คือ invalid_request
  • คำตอบ: { "outcome": {...}, "data": <transition_result> } — $defs/transition_result

Outcome:

  • transition_applied / idempotent_replay
  • invalid_request
  • capability_refused
  • requester_unauthorized — delegation ไม่เป็นที่รู้จัก ไม่ครอบคลุมรายการนี้ หรือบุคคลของมันไม่มีอำนาจที่ transition นั้นต้องการอีกต่อไป
  • content_not_found
  • scheduled_target_stale
  • workflow_conflict
  • dependent_content
  • idempotency_key_reused
  • transition_unavailable / transition_refused
  • rate_limited
  • temporary_failure

เผยแพร่ draft ที่ถูก pin

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0001:r1" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000001",
"expected_workflow_revision": 0,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "transition_applied",
"message": "The transition was applied.",
"retry": false
},
"data": {
"instance_id": "cdocsinstance00000000000001",
"primary_instance_id": "cdocsinstance00000000000001",
"from": "draft",
"to": "published",
"published": true,
"published_changed": true,
"published_version_id": "cdocsversion00000000000001",
"revision": 1,
"redirect": {
"status": "none",
"reason": "no_public_address"
},
"policy_results": []
}
}

คำสั่งเดิมอีกครั้ง

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0001:r1" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000001",
"expected_workflow_revision": 0,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "idempotent_replay",
"message": "This command was already applied. The first result is returned.",
"retry": false
},
"data": {
"instance_id": "cdocsinstance00000000000001",
"primary_instance_id": "cdocsinstance00000000000001",
"from": "draft",
"to": "published",
"published": true,
"published_changed": true,
"published_version_id": "cdocsversion00000000000001",
"revision": 1,
"redirect": {
"status": "none",
"reason": "no_public_address"
},
"policy_results": []
}
}

การ retry หลัง network ล้มเหลวปลอดภัย: ผลลัพธ์แรกทั้งหมดจะกลับมา รวม redirect และไม่มีอะไรรันซ้ำสองครั้ง

key เดิม คำสั่งต่างกัน

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0001:r1" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000001",
"expected_workflow_revision": 7,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "idempotency_key_reused",
"message": "This idempotency key was already used for a different command.",
"retry": false
}
}

ให้สร้าง key จากรายการตั้งเวลาและ revision ของมัน การตั้งเวลาใหม่คือ revision ใหม่ จึงเป็น key ใหม่

ยกเลิกการเผยแพร่ version ที่ live และถูก pin

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/draft" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0002:r1" \
--data '{
"kind": "unpublish",
"expected_published_version_id": "cdocsversion00000000000001",
"expected_workflow_revision": 1,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "transition_applied",
"message": "The transition was applied.",
"retry": false
},
"data": {
"instance_id": "cdocsinstance00000000000001",
"primary_instance_id": "cdocsinstance00000000000001",
"from": "published",
"to": "draft",
"published": false,
"published_changed": true,
"published_version_id": null,
"revision": 2,
"policy_results": []
}
}

draft ที่ใหม่กว่าจะไม่ถูกเผยแพร่อย่างเงียบ ๆ เลย

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0003:r1" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000001",
"expected_workflow_revision": 2,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "scheduled_target_stale",
"message": "The pinned version is no longer the one this transition would move.",
"retry": false
}
}

มีคนบันทึก draft ที่ใหม่กว่าหลังจากสร้างรายการตั้งเวลาแล้ว เป็นสถานะสุดท้าย: บุคคลเป็นผู้ตัดสินใจว่าจะทำอย่างไร

workflow ที่เปลี่ยนไปจะไม่ถูกเดินผ่านอย่างเงียบ ๆ เลย

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0004:r1" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000002",
"expected_workflow_revision": 0,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "workflow_conflict",
"message": "The content moved through its workflow after this was requested.",
"retry": false
}
}

shape ของ request เป็นแบบปิด

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched:plg_0000000000000000000000000000d1ee:sch_0005:r1" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000002",
"expected_workflow_revision": 2,
"delegation_id": "pld_0000000000000000000000000000d1ee",
"override_dependents": true
}'
{
"outcome": {
"code": "invalid_request",
"message": "The request is not valid for protocol v1.",
"retry": false
}
}

ไม่มีการ override ใน protocol v1 key ที่ไม่รู้จักจะถูกปฏิเสธ ไม่ใช่ถูกทิ้ง

mutation ที่ไม่มี idempotency key

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/transitions/published" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
--data '{
"kind": "publish",
"expected_current_version_id": "cdocsversion00000000000002",
"expected_workflow_revision": 2,
"delegation_id": "pld_0000000000000000000000000000d1ee"
}'
{
"outcome": {
"code": "invalid_request",
"message": "Idempotency-Key is required.",
"retry": false
}
}

บันทึกค่าฟิลด์ในนามบุคคล การบันทึก lifecycle ปกติของฟิลด์ค่าที่ระบุชื่อของ item หนึ่งรายการ ทำในนามบุคคลที่มอบสิทธิ์: สิทธิ์ต่อโมเดล ความเป็นเจ้าของ และบัญชีปัจจุบันของเขาถูกอ่านใหม่ และการบันทึกเป็นของเขาในประวัติเนื้อหา การบันทึกนี้ถูก pin: expected_version_id ต้องยังเป็นเวอร์ชันปัจจุบัน มิฉะนั้นคำตอบคือ version_conflict และไม่มีอะไรถูกเขียน กฎเดียวกับที่ editor ใช้เป็นตัวตัดสินค่า — ชนิดฟิลด์ ความไม่ซ้ำ ลำดับชั้น ภาษา และขนาดเวอร์ชัน fields รับค่าของฟิลด์ได้ไม่เกิน 50 ฟิลด์ โดยค่ารวมมีขนาดไม่เกิน 65536 ไบต์ ส่งการอ้างอิงแบบมีชนิดแยกใน relations, locale_relations และ media เมื่อเปลี่ยนการอ้างอิง fields ว่างได้ ถ้าไม่มี locale ค่าจะเป็นของภาษาของ item ที่ระบุเอง ฟิลด์ slug ที่แปลได้จะเปลี่ยนที่อยู่ของภาษานั้นเท่านั้น ส่วนฟิลด์ที่ใช้ร่วมกันระบุที่อยู่ของภาษาหลัก slugs ในคำตอบคือที่อยู่ของทุกภาษา ประวัติฝั่งผู้ดูแลบันทึกบุคคลนั้นพร้อมระบุ installation นี้

  • ผู้เรียก: ปลั๊กอิน, content:write
  • ต้องการ: content:write
  • Idempotency-Key (บังคับ) — เหมือนกับ transition การส่งซ้ำได้ผลลัพธ์แรกกลับมา
  • Body — $defs/content_write_command แบบปิด
  • คำตอบ: { "outcome": {...}, "data": <save_result> } — $defs/content_save_result

Outcome:

  • content_saved / idempotent_replay
  • invalid_request
  • capability_refused
  • requester_unauthorized — delegation ไม่ครอบคลุม item นี้ หรือบุคคลของมันแก้ไข item นี้ไม่ได้ในตอนนี้
  • content_not_found
  • version_conflict
  • validation_failed — data ระบุ reason ของกฎ และ fields เมื่อมี
  • limit_exceeded
  • idempotency_key_reused
  • rate_limited
  • temporary_failure

บันทึกฟิลด์เดียว โดย pin กับเวอร์ชันปัจจุบัน

หน้าต่างเทอร์มินัล
curl -X PUT "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: write:plg_0000000000000000000000000000d1ee:0001" \
--data '{
"delegation_id": "pld_0000000000000000000000000000d1ee",
"expected_version_id": "cdocsversion00000000000002",
"fields": {
"title": "Hello from the plugin"
}
}'
{
"outcome": {
"code": "content_saved",
"message": "The field values were saved as a new version.",
"retry": false
},
"data": {
"instance_id": "cdocsinstance00000000000001",
"primary_instance_id": "cdocsinstance00000000000001",
"translation_group_id": "cdocsgroup0000000000000001",
"version": {
"id": "cdocsversion00000000000003",
"version_number": 3
},
"current_version_id": "cdocsversion00000000000003",
"published_version_id": null,
"slugs": {
"en": "hello",
"fr": "hello"
},
"locales": {
"en": "cdocsinstance00000000000001",
"fr": "cdocsinstancefr000000000001"
},
"warnings": [],
"policy_results": []
}
}

เวอร์ชันใหม่เป็นของบุคคลที่มอบสิทธิ์ pin current_version_id สำหรับการเขียนครั้งถัดไป

การเขียนที่ pin กับเวอร์ชันที่เก่ากว่า

หน้าต่างเทอร์มินัล
curl -X PUT "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: write:plg_0000000000000000000000000000d1ee:0002" \
--data '{
"delegation_id": "pld_0000000000000000000000000000d1ee",
"expected_version_id": "cdocsversion00000000000001",
"fields": {
"title": "Lost update"
}
}'
{
"outcome": {
"code": "version_conflict",
"message": "The entry changed after the pinned version. Read it again before writing.",
"retry": false
}
}

ไม่มีอะไรถูกเขียน อ่าน item ใหม่แล้วตัดสินใจร่วมกับบุคคลนั้น

ข้อเสนอของ installation นี้สำหรับ item หนึ่งรายการ ข้อเสนอที่ installation นี้สร้างให้ item ใหม่สุดก่อน ไม่เกิน 50 รายการ ในทุกสถานะ: open, applied หรือ dismissed ข้อเสนอของ installation อื่นหรือของ management credential จะไม่แสดง

  • ผู้เรียก: ปลั๊กอิน, content:propose
  • ต้องการ: content:propose
  • ไม่มี query ไม่มี body
  • คำตอบ: { "data": [<proposal>] } — $defs/proposal_list

Outcome:

  • capability_refused
  • content_not_found
  • invalid_request
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

เสนอค่าฟิลด์ให้ผู้แก้ไขรายการตรวจทาน บันทึกค่าที่เสนอสำหรับฟิลด์ที่ระบุชื่อ โดย pin กับ base_version_id เพื่อให้ผู้แก้ไขรายการตรวจทาน เนื้อหาไม่เปลี่ยน จนกว่าผู้ใช้จะนำข้อเสนอไปใช้ใน Dee Wan การกด ใช้ เป็นการบันทึกตามปกติของผู้ใช้นั้น ด้วยสิทธิ์ของตนและเทียบกับเวอร์ชันที่ pin ไว้ ตัวแก้ไขแสดงชื่อปลั๊กอินและบุคคลที่ปลั๊กอินทำแทน บุคคลที่มอบสิทธิ์ต้องยังอ่านรายการได้

  • ผู้เรียก: ปลั๊กอิน, content:propose
  • ต้องการ: content:propose
  • Idempotency-Key (บังคับ) — เหมือนกับ transition
  • Body — $defs/proposal_command แบบปิด
  • คำตอบ: { "outcome": {...}, "data": <proposal> } — $defs/proposal

Outcome:

  • proposal_created / idempotent_replay
  • invalid_request
  • capability_refused
  • requester_unauthorized
  • content_not_found
  • version_conflict — base_version_id ไม่ใช่เวอร์ชันปัจจุบันแล้ว
  • validation_failed
  • limit_exceeded
  • idempotency_key_reused
  • rate_limited
  • temporary_failure

เสนอค่าให้ editor ตรวจ

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/content/cdocsinstance00000000000001/proposals" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: propose:plg_0000000000000000000000000000d1ee:0001" \
--data '{
"delegation_id": "pld_0000000000000000000000000000d1ee",
"base_version_id": "cdocsversion00000000000003",
"fields": {
"title": "A better title"
},
"note": "Shorter and clearer."
}'
{
"outcome": {
"code": "proposal_created",
"message": "The proposal was created. Nothing changes until a person applies it.",
"retry": false
},
"data": {
"id": "prop_00000000000000000000000000000d1e",
"revision": 0,
"instance_id": "cdocsinstance00000000000001",
"lang": "en",
"base_version_id": "cdocsversion00000000000003",
"fields": {
"title": "A better title"
},
"note": "Shorter and clearer.",
"source": {
"kind": "plugin",
"id": "plg_0000000000000000000000000000d1ee",
"label": "My Plugin",
"requested_by": "[email protected]"
},
"state": "open",
"applied_version_id": null,
"created_at": "2026-01-01T00:00:00.000Z",
"resolved_at": null
}
}

ไม่มีอะไรเปลี่ยน editor ของรายการแสดงข้อเสนอนี้ว่ามาจาก My Plugin ตามคำขอของบุคคลนั้น

ข้อเสนอหนึ่งรายการที่ installation นี้สร้าง อ่านข้อเสนอหนึ่งรายการเพื่อรู้ว่าบุคคลนำไปใช้หรือปัดทิ้ง และการนำไปใช้สร้างเวอร์ชันใด ข้อเสนอของ installation อื่น หรือของ item นอก grant คือ content_not_found

  • ผู้เรียก: ปลั๊กอิน, content:propose
  • ต้องการ: content:propose
  • ไม่มี query ไม่มี body
  • คำตอบ: { "data": <proposal> } — $defs/proposal

Outcome:

  • capability_refused
  • content_not_found
  • invalid_request
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

อ่าน working draft ของบุคคล ฉบับร่างที่ยังไม่บันทึกในตัวแก้ไขของบุคคลที่มอบสิทธิ์ สำหรับรายการและภาษาหนึ่ง ซึ่งเป็นงานที่ตัวแก้ไขรายการใช้กู้คืน draft ไม่เคยเป็นเวอร์ชันและไม่เคยเป็น audit มันเป็นของบุคคลนั้น: ปลั๊กอินอ่านได้เฉพาะ draft ของบุคคลที่เปิดปลั๊กอิน target คือ id ของ item หรือ new:<model id> สำหรับ item ที่ยังไม่ถูกสร้าง ซึ่งต้องใช้ delegation แบบ manage บุคคลนั้นต้องยังแก้ไข item ได้ (หรือสร้าง item ของโมเดลนั้นได้) draft หมดอายุหลัง 30 วัน

  • ผู้เรียก: ปลั๊กอิน, draft:write
  • ต้องการ: draft:write
  • delegation_id, target, locale — บังคับทั้งหมด ไม่มีอย่างอื่น $defs/draft_query
  • คำตอบ: { "data": <draft|null>, "expired": <boolean> } — $defs/draft_read

Outcome:

  • capability_refused
  • content_not_found
  • requester_unauthorized
  • invalid_request
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

เขียน working draft ของบุคคล แทนที่ฉบับร่างนั้นเฉพาะเมื่อรีวิชันปัจจุบันตรงกับ expected_revision (0 เมื่อยังไม่มีฉบับร่าง) ถ้าอุปกรณ์หรือแท็บอื่นเขียนก่อนคือ draft_conflict พร้อม draft ปัจจุบันใน data.current draft หนึ่งเก็บได้ไม่เกิน 1900000 ไบต์ และบุคคลหนึ่งเก็บได้ไม่เกิน 200 รายการต่อไซต์ เกินอย่างใดอย่างหนึ่งคือ limit_exceeded data คือสถานะการทำงานของ editor Dee Wan ตรวจสอบมันเฉพาะเมื่อบุคคลบันทึก

  • ผู้เรียก: ปลั๊กอิน, draft:write
  • ต้องการ: draft:write
  • Idempotency-Key (บังคับ)
  • Body — $defs/draft_write_command แบบปิด
  • คำตอบ: { "outcome": {...}, "data": <draft> } — $defs/working_draft

Outcome:

  • draft_saved / idempotent_replay
  • draft_conflict
  • limit_exceeded
  • invalid_request
  • capability_refused
  • requester_unauthorized
  • content_not_found
  • idempotency_key_reused
  • rate_limited
  • temporary_failure

ทิ้ง working draft ของบุคคล ลบ revision ของ draft นั้นตรงตัวเท่านั้น revision ที่ใหม่กว่าจะถูกเก็บไว้และได้คำตอบ draft_conflict draft ที่ไม่มีอยู่แล้วคือ draft_discarded

  • ผู้เรียก: ปลั๊กอิน, draft:write
  • ต้องการ: draft:write
  • Idempotency-Key (บังคับ)
  • Body — $defs/draft_discard_command แบบปิด
  • คำตอบ: { "outcome": {...} }

Outcome:

  • draft_discarded / idempotent_replay
  • draft_conflict
  • invalid_request
  • capability_refused
  • requester_unauthorized
  • content_not_found
  • idempotency_key_reused
  • rate_limited
  • temporary_failure

อัปโหลด media ในนามบุคคล การอัปโหลด media ปกติ ผ่านการตรวจสอบและที่เก็บเดียวกับคลัง media: ไบต์ต้องเป็นหนึ่งใน media_types ของประเภทที่ไซต์รับ (ค่าเริ่มต้นคือภาพและเอกสาร เสียงและวิดีโอเมื่อไซต์เปิดใช้) ขนาดไม่เกิน 33554432 ไบต์ บุคคลที่มอบสิทธิ์ต้องมี media:upload delegation ใดก็ได้ของ installation นี้อัปโหลดได้ ส่ง multipart/form-data ที่มี part เหล่านี้พอดี: file, delegation_id และถ้าต้องการ alt_texts (JSON object จากรหัสภาษาของไซต์ไปยังข้อความ), width, height, on_duplicate part อื่นใดคือ invalid_request ไฟล์ที่ถูกปฏิเสธคือ validation_failed พร้อม reason ของกฎ media เมื่อไซต์มีไบต์เดียวกันอยู่แล้ว on_duplicate เป็นตัวตัดสิน: ask (ค่าเริ่มต้น) ตอบ media_duplicate พร้อมไฟล์ที่มีอยู่และไม่อัปโหลดอะไร use_existing ตอบ media_reused พร้อมไฟล์ที่มีอยู่ copy อัปโหลดสำเนาแยก

  • ผู้เรียก: ปลั๊กอิน, media:write
  • ต้องการ: media:write
  • Idempotency-Key (บังคับ)
  • Body — multipart, $defs/media_upload_form
  • คำตอบ: { "outcome": {...}, "data": <media> } — $defs/media; media_duplicate มี $defs/media_duplicate

Outcome:

  • media_uploaded / media_reused / idempotent_replay
  • media_duplicate — ไซต์มีไบต์เหล่านี้อยู่แล้วและ on_duplicate เป็น ask
  • invalid_request
  • capability_refused
  • requester_unauthorized
  • validation_failed
  • limit_exceeded
  • idempotency_key_reused
  • content_not_found — การส่งซ้ำที่ media ถูกลบไปแล้ว
  • rate_limited
  • temporary_failure

แลก launch code เป็น delegation เบราว์เซอร์มาถึง management_url พร้อม dee_wan_launch BACKEND ของปลั๊กอินส่งรหัสนั้นมาที่นี่ พร้อม bearer token ของตัวเอง ภายใน 60 วินาที รหัสถูกใช้ได้ครั้งเดียว สิ่งที่ได้กลับมาคือการผูก — installation, ไซต์, action, content instance (ไม่บังคับ) และ model_ids: โมเดลใน grant ที่บุคคลนั้นอ่านได้ตอน launch ทุกรายการหรือเฉพาะรายการของตัวเอง (สำหรับ action ของเนื้อหา คือโมเดลของรายการนั้น) — และตัวตนที่ปลอดภัยต่อการแสดงผลสำหรับ session ของปลั๊กอินเอง แสดงและกระทำเฉพาะภายใน model_ids และ /delegations/:delegationId บอกสิ่งที่ยังใช้ได้ในตอนนี้ มันไม่ได้มอบ capability ใดของ Dee Wan ที่ token ของ installation ยังไม่มีอยู่แล้ว และรหัสที่ออกให้ installation อื่นจะถูกปฏิเสธ ไม่ต้องใช้ Idempotency-Key: ตัวรหัสเองใช้ได้ครั้งเดียว ดังนั้นการ retry หลัง response หายไปจะตอบ invalid_request และบุคคลนั้นต้อง launch ใหม่

  • ผู้เรียก: ปลั๊กอิน พร้อม token ของ installation
  • ต้องการ: ไม่มี
  • Body — $defs/launch_exchange_request ไม่มี Idempotency-Key
  • คำตอบ: { "data": <delegation> } — $defs/launch_delegation

Outcome:

  • invalid_request — ไม่รู้จัก, ถูกใช้แล้ว, หมดอายุ หรือออกให้ installation อื่น
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

เปลี่ยน launch code เป็น delegation

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/launch/exchange" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
--data '{
"code": "dwl_ExampleLaunchCodeExampleLaunchCodeExampleLa"
}'
{
"data": {
"delegation_id": "pld_0000000000000000000000000000d1ee",
"installation_id": "plg_0000000000000000000000000000d1ee",
"site_id": "cdocssite00000000000000001",
"action": "schedule",
"instance_id": "cdocsinstance00000000000001",
"model_ids": [
"cdocsmodel0000000000000001"
],
"user": {
"id": "cdocsuser00000000000000001",
"email": "[email protected]"
}
}
}

เก็บ delegation_id ไว้ ทุกคำสั่งหลังจากนี้จะแนบมันไป และ core จะอ่านอำนาจของบุคคลนั้นซ้ำ

launch code ใช้ได้ครั้งเดียว

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/launch/exchange" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
--data '{
"code": "dwl_ExampleLaunchCodeExampleLaunchCodeExampleLa"
}'
{
"outcome": {
"code": "invalid_request",
"message": "The launch code is unknown, used or expired.",
"retry": false
}
}

แลกรหัสการเรียกใช้เป็นข้อมูลเข้าที่ผูกไว้ core POST $defs/invocation_envelope ไปยัง endpoint ของการดำเนินการหรือนโยบายที่ประกาศไว้ ก่อนตอบ POST นั้น ปลั๊กอินส่ง code ของซองมาที่นี่พร้อม bearer token ของตัวเอง รหัสถูกใช้ได้ครั้งเดียว สิ่งที่ได้กลับคือข้อมูลที่ core บันทึกไว้: id ของการดำเนินการหรือนโยบาย โมเดลและรายการ (ถ้ามี) ตัวตนสำหรับแสดงของผู้ที่ขอ และ input ที่ผูกไว้ — ค่าของการดำเนินการ หรือสำหรับนโยบายคือ $defs/policy_facts ข้อมูลผูกของการดำเนินการกับรายการยังมี delegation_id: การมอบสิทธิ์แบบถาวรที่ผูกกับผู้เรียก ไซต์ การติดตั้ง โมเดล และรายการ เก็บไว้เหมือน launch delegation สำหรับการอ่านและคำสั่ง transition ของรายการนั้นในภายหลัง core อ่านสิทธิ์ของบุคคลนั้นใหม่ทุกครั้งที่ใช้ ข้อมูลที่ผูกของการดำเนินการระดับไซต์มี delegation แบบเดียวกับ launch แบบ manage: ไม่มีรายการ และมีโมเดลที่ได้รับสิทธิ์ซึ่งผู้เรียกอ่านได้ นโยบายได้ null core รับคำตอบของ POST จากการติดตั้งที่แลกรหัสการเรียกใช้นี้เท่านั้น และเฉพาะตอนที่การติดตั้งนั้นยังครอบคลุมอยู่ ทุกอย่างเกิดขึ้นภายใน 10 วินาที

  • ผู้เรียก: ปลั๊กอิน พร้อมโทเค็นการติดตั้ง ระหว่างตอบการเรียกใช้
  • ต้องมี: ไม่มี
  • Body — $defs/invocation_exchange_request ไม่มี Idempotency-Key
  • ตอบ: { "data": <binding> } — $defs/invocation_binding

ผลลัพธ์:

  • invalid_request — ไม่รู้จัก ใช้ไปแล้ว หมดอายุ หรือออกให้การติดตั้งอื่น
  • capability_refused — การติดตั้งถูกลดสิทธิ์ หรือ manifest เปลี่ยน หลังจาก core ขอ
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

รหัสการเรียกใช้ใช้ได้ครั้งเดียว และเฉพาะกับการติดตั้งที่ออกให้

หน้าต่างเทอร์มินัล
curl -X POST "https://cms.example/api/plugin/v1/invocations/exchange" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp" \
-H "Content-Type: application/json" \
--data '{
"code": "dwi_ExampleInvocationCodeExampleInvocationCodeE"
}'
{
"outcome": {
"code": "invalid_request",
"message": "The invocation code is unknown, used or expired.",
"retry": false
}
}

อ่านว่าตอนนี้ delegation เข้าถึงอะไรได้ model_ids ของ delegation ที่ถูกจำกัดให้แคบลงตาม grant ของโมเดลปัจจุบันและตามสิ่งที่บุคคลนั้นทำได้ ในตอนนี้ โดยอ่านซ้ำในทุก request delegation แบบ manage แสดงทุกโมเดลที่บุคคลนั้นยังอ่านได้ ทุกรายการ หรือเฉพาะรายการของตัวเองภายใต้ read:own delegation แบบ content-action แสดงโมเดลของรายการนั้นตราบที่บุคคลนั้นยังอ่านรายการนั้นได้ kinds บอกชนิดของ transition ที่บุคคลนั้นทำได้กับรายการของโมเดลนั้น: installation มี workflow:publish / workflow:unpublish บุคคลนั้นแก้ไขรายการได้ และ workflow ของโมเดลมีเส้นทางชนิดนั้นที่บุคคลนั้นมีสิทธิ์ entries บอกว่าเงื่อนไขนี้เป็นจริงกับทุกรายการหรือไม่ all: บุคคลนั้นอ่านได้ทุกรายการของโมเดล และ kinds ใช้กับทุกรายการ checked: บุคคลนั้นอ่านหรือแก้ไขได้เฉพาะรายการของตัวเองที่นั่น ดังนั้น kinds คือสิ่งที่มากที่สุดที่บุคคลนั้นทำได้กับรายการที่แก้ไขได้ ให้ถาม /delegations/:delegationId/entries ว่าจะแสดงรายการใดและแต่ละรายการทำอะไรได้ ใช้เพื่อแสดงเฉพาะสิ่งที่บุคคลนั้นเห็นได้ และเสนอการย้ายเวลาหรือยกเลิก transition ที่ตั้งไว้เฉพาะเมื่อชนิดนั้นอยู่ในรายการ delegation ที่เปิดโดย credential ของ management แสดงเฉพาะชนิดที่อยู่ในเพดาน capability ของมัน คำสั่งยังถูกตรวจแยกต่างหาก

  • ผู้เรียก: ปลั๊กอิน พร้อม token ของ installation
  • ต้องการ: ไม่มี
  • ไม่มี query และไม่มี body
  • คำตอบ: { "data": { "delegation_id", "models": [{ "id", "slug", "kinds", "entries" }] } } — $defs/delegation_models

Outcome:

  • requester_unauthorized — delegation ไม่เป็นที่รู้จัก เป็นของ installation อื่น หรือบุคคลของมันถูกปิดใช้งานหรือไม่ได้เป็นสมาชิกของไซต์อีกต่อไป
  • invalid_request
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

ตอนนี้ delegation เข้าถึงอะไรได้

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/delegations/pld_0000000000000000000000000000d1ee" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"data": {
"delegation_id": "pld_0000000000000000000000000000d1ee",
"models": [
{
"id": "cdocsmodel0000000000000001",
"slug": "article",
"kinds": [
"publish",
"unpublish"
],
"entries": "all"
}
]
}
}

ถามอีกครั้งก่อนทุกหน้าและทุกการกระทำ บุคคลที่เสียโมเดลหรือสิทธิ์ไปก็จะเสียที่นี่ด้วย

อ่านว่าตอนนี้ delegation เข้าถึงรายการใดได้ ใช้กับโมเดลที่ /delegations/:delegationId ระบุ entries: "checked" ระบุ id ของรายการได้ไม่เกิน 100 ตัว คำตอบแสดงเฉพาะรายการที่บุคคลของ delegation อ่านได้ ในตอนนี้ แต่ละรายการพร้อมชนิดของ transition ที่บุคคลนั้นทำได้กับรายการนั้น: kinds ของโมเดลเมื่อบุคคลนั้นแก้ไขรายการนั้นได้ ไม่เช่นนั้นไม่มีเลย รายการของไซต์อื่น รายการในถังขยะ รายการของโมเดลที่อยู่นอก model_ids ของ delegation หรือนอก grant ปัจจุบัน รายการที่อยู่นอก translation group ของ delegation แบบ content-action หรือรายการที่บุคคลนั้นอ่านไม่ได้ จะถูกตัดออกโดยไม่บอกเหตุผล ความเป็นเจ้าของและสิทธิ์ถูกอ่านซ้ำในทุก request ดังนั้นให้ถามอีกครั้งก่อนทุกหน้าและทุกการกระทำ เก็บคำตอบไว้เฉพาะบุคคลนั้น: อย่าแสดงรายการที่คำตอบตัดออกเด็ดขาด

  • ผู้เรียก: ปลั๊กอิน พร้อม token ของ installation
  • ต้องการ: ไม่มี
  • instance_id — 1 ถึง 100 ครั้ง แต่ละตัวเป็น id ของรายการ ค่าซ้ำนับครั้งเดียว ไม่มี query อื่นและไม่มี body
  • คำตอบ: { "data": { "delegation_id", "entries": [{ "instance_id", "model_id", "kinds" }] } } — $defs/delegation_entries

Outcome:

  • invalid_request — ไม่มี instance_id มีเกิน 100 ตัว มีตัวที่รูปแบบไม่ถูกต้อง หรือมี parameter อื่น
  • requester_unauthorized — delegation ไม่เป็นที่รู้จัก เป็นของ installation อื่น หรือบุคคลของมันถูกปิดใช้งานหรือไม่ได้เป็นสมาชิกของไซต์อีกต่อไป
  • plugin_unauthorized
  • rate_limited
  • temporary_failure

ตอนนี้ delegation เข้าถึงรายการใดในชุดนี้ได้

หน้าต่างเทอร์มินัล
curl "https://cms.example/api/plugin/v1/delegations/pld_0000000000000000000000000000d1ee/entries?instance_id=cdocsinstance00000000000001&instance_id=cotherinstance0000000000001" \
-H "Authorization: Bearer dwp_ExampleTokenNotRealExampleTokenNotRealExamp"
{
"data": {
"delegation_id": "pld_0000000000000000000000000000d1ee",
"entries": [
{
"instance_id": "cdocsinstance00000000000001",
"model_id": "cdocsmodel0000000000000001",
"kinds": [
"publish",
"unpublish"
]
}
]
}
}

รายการของไซต์อื่นถูกตัดออกโดยไม่บอกเหตุผล แสดงเฉพาะรายการที่อยู่ในคำตอบ

สร้างรายการที่บันทึกแล้ว ตรวจด้วย lifecycle ปกติ delegation แบบ manage ต้องรวมโมเดลนี้

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: content:create และสิทธิ์สร้างปัจจุบัน
  • ต้องมี Idempotency-Key
  • เนื้อหา: $defs/content_create_command.
  • คำตอบ: content_saved พร้อม $defs/content_save_result.

ผลลัพธ์:

  • content_saved / idempotent_replay
  • capability_refused / requester_unauthorized / content_not_found / validation_failed / idempotency_key_reused

ย้ายรายการไปถังขยะ ใช้ lifecycle กับทั้งรายการ ต้องยกเลิกเผยแพร่ก่อน ตรวจเวอร์ชันปัจจุบันแบบ atomic

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: content:delete และสิทธิ์ลบปัจจุบัน
  • ต้องมี Idempotency-Key
  • เนื้อหา: $defs/content_delete_command.
  • คำตอบ: content_deleted พร้อมผลการย้ายไปถังขยะตามปกติ

ผลลัพธ์:

  • content_deleted / idempotent_replay
  • capability_refused / requester_unauthorized / content_not_found / version_conflict / validation_failed / idempotency_key_reused

แสดงเวอร์ชันที่เก็บไว้ แบ่งหน้า ID เวอร์ชันแบบจำกัด ตรวจความเป็นเจ้าของรายการหลัก

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: content:read และสิทธิ์อ่านประวัติปัจจุบัน
  • Query: ต้องมี delegation_id; limit, cursor ไม่บังคับ
  • คำตอบ: $defs/content_versions.

ผลลัพธ์:

  • 200
  • capability_refused / requester_unauthorized / content_not_found / invalid_request

อ่านเวอร์ชันที่เก็บไว้ snapshot มาตรฐานถูกจำกัดตามสิทธิ์ฟิลด์ ภาษา และความเป็นเจ้าของ เมื่อส่ง include_dependencies=true คำตอบจะเพิ่ม dependency_refs: [{ instance_id, locale }] และ dependencies_incomplete โดยรวมความสัมพันธ์ทั่วไป ความสัมพันธ์แยกภาษา ความสัมพันธ์ในคอมโพเนนต์ซ้อน และส่วนที่ใช้ร่วมกันซึ่งฝังไว้ ระบบใช้สิทธิ์ปัจจุบันของผู้ใช้และโมเดลที่อนุญาตให้ปลั๊กอินเข้าถึง ไม่ส่งตัวระบุของรายการที่อ่านไม่ได้ และระบุ dependencies_incomplete: true แทน รายการอ้างอิงนี้ไม่ให้สิทธิ์อ่านหรือเผยแพร่เพิ่ม

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: content:read และสิทธิ์อ่านประวัติปัจจุบัน
  • Query: ต้องมี delegation_id
  • พารามิเตอร์เสริมคือ include_dependencies=true และ dependency_locale=<code> การระบุภาษาต้องใช้ร่วมกับพารามิเตอร์แรก และเป็นภาษาของไซต์ที่ผู้ใช้อ่านได้ หากไม่ระบุภาษา ระบบตรวจภาษาของเวอร์ชันที่อ่านได้
  • คำตอบ: {data: <content_version>}; $defs/content_version.

ผลลัพธ์:

  • 200
  • capability_refused / requester_unauthorized / content_not_found / invalid_request

อ่าน metadata ของสื่อที่มีอยู่ ใช้บริการทรัพยากรสื่อร่วมกันสำหรับสื่อของไซต์

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: media:read และสิทธิ์อ่านคลังสื่อปัจจุบัน
  • Query: ต้องมี delegation_id
  • คำตอบ: {data: <media>} พร้อม metadata สำหรับแสดงผล

ผลลัพธ์:

  • 200
  • capability_refused / requester_unauthorized / content_not_found / invalid_request

อ่านไบต์ของสื่อที่มีอยู่ ใช้ reader ของ provider ร่วมกันสำหรับไฟล์สาธารณะและส่วนตัวของไซต์ การอ้างอิงภายนอกไม่มีไบต์ในไซต์ คำตอบส่วนตัวห้าม cache

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: media:read และสิทธิ์อ่านคลังสื่อปัจจุบัน
  • Query: ต้องมี delegation_id
  • คำตอบ: ไฟล์ไบนารีพร้อม content type/disposition ที่ปลอดภัย

ผลลัพธ์:

  • 200
  • capability_refused / requester_unauthorized / content_not_found / limit_exceeded / temporary_failure

แก้ metadata ของสื่อ ใช้การตรวจร่วมกันและเขียนแบบ atomic พร้อม idempotency

  • ผู้เรียก: ปลั๊กอินในนามบุคคลที่มอบสิทธิ์
  • ต้องการ: media:update และสิทธิ์ media:update ปัจจุบัน
  • ต้องมี Idempotency-Key
  • เนื้อหา: $defs/media_update_command.
  • คำตอบ: media_updated, {id}.

ผลลัพธ์:

  • media_updated / idempotent_replay
  • capability_refused / requester_unauthorized / content_not_found / validation_failed / idempotency_key_reused

เตรียมเวอร์ชันอนาคตแยกจากฉบับร่างปกติ สร้างเวอร์ชันที่บันทึกไว้จากฉบับร่างปัจจุบันของรายการ แล้วผูกกับการติดตั้งปลั๊กอินและรีลีสนี้ การเรียกซ้ำด้วยการติดตั้ง รีลีส และรายการหลักเดิมจะคืนเวอร์ชันเดิม โดยไม่เปลี่ยนฉบับร่างปกติหรือค่า EAV ผู้มอบสิทธิ์ยังต้องอ่านและแก้ไขรายการได้ หากระบุ locale ระบบจะตรวจสิทธิ์ของภาษานั้นด้วย ชื่อจะถูกตัดช่องว่างหัวท้ายและยาวได้ไม่เกิน 120 อักขระ แก้ไขเวอร์ชันนี้ใน Dee Wan ผ่าน editor_url โดยมี revision และการตรวจเวอร์ชันของตัวเอง เฉพาะคำสั่งเผยแพร่ที่มีสิทธิ์ปัจจุบันเท่านั้นที่ตรึงเวอร์ชันเพื่อดำเนินการได้

  • ผู้เรียก: ปลั๊กอินแทนผู้มอบสิทธิ์
  • ต้องมี: workflow:release และสิทธิ์แก้ไขที่ได้รับมอบและยังใช้ได้
  • Body — $defs/future_create_command เป็นรูปแบบปิด ต้องมี delegation_id, release_id, name และระบุ locale ได้ ไม่ต้องใช้ Idempotency-Key
  • คำตอบ: 201 { "data": <future with editor_url> } — $defs/future_created รวมถึงเมื่อเรียกซ้ำแล้วได้เวอร์ชันเดิม

Outcome:

  • invalid_request
  • capability_refused / requester_unauthorized / content_not_found
  • validation_failed / version_conflict / transition_refused / temporary_failure

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

  • ผู้เรียก: ปลั๊กอินแทนผู้มอบสิทธิ์
  • ต้องมี: workflow:release และสิทธิ์อ่านที่ได้รับมอบและยังใช้ได้
  • Query: ต้องมี delegation_id — $defs/future_read_query
  • คำตอบ: 200 { "data": { "future": <future>, "summary": <content_summary> } } — $defs/future_read

Outcome:

  • invalid_request
  • capability_refused / requester_unauthorized / content_not_found

ใช้รีลีสที่ประสานกัน. วางแผนสถานะสุดท้ายของคำสั่งไม่เกิน 25 รายการในเว็บไซต์เดียว ตรวจสิทธิ์ ขอบเขตโมเดล workflow เวอร์ชัน ภาษา และความสัมพันธ์ ข้อมูลเผยแพร่ เหตุการณ์ workflow และบันทึกการเรียกซ้ำ commit ในชุดคำสั่งที่มี guard เดียวในฐานข้อมูลเว็บไซต์ หรือไม่เปลี่ยนเลย รองรับหลายภาษาที่ต่างกันของกลุ่มเดียว แต่ปฏิเสธขอบเขตที่ทับกัน การส่งแจ้งเตือนและ redirect อัตโนมัติเกิดหลัง commit

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: workflow:release
  • ต้องมี Idempotency-Key body คือ $defs/release_command
  • คำตอบ: release_applied หรือ idempotent_replay พร้อม $defs/release_result

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

อ่านความคิดเห็นผู้เข้าชม. ใช้บริการอ่านเพื่อจัดการความคิดเห็นเดิม และการมองเห็นโมเดลกับรายการปัจจุบัน

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: comments:read
  • query มี delegation_id และตัวกรองรายการความคิดเห็นของ API การจัดการเดิม
  • คำตอบ: รายการความคิดเห็นที่มีขีดจำกัด โดยไม่เปิดเผยอีเมลส่วนตัว

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

จัดการความคิดเห็นผู้เข้าชม. ใช้บริการจัดการความคิดเห็นเดิม ไม่สร้างความยินยอมขึ้นเอง และปฏิเสธสถานะเก่าของแต่ละความคิดเห็น

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: comments:moderate
  • ต้องมี Idempotency-Key body มี delegation_id, to, comments: [{id, expected}]
  • คำตอบ: ผลการจัดการความคิดเห็นตามปกติ

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

อ่าน redirect. ต้องมีสิทธิ์ site:settings ปัจจุบัน

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: redirects:read
  • query มี delegation_id
  • คำตอบ: เฉพาะ revision และ redirects ที่ยอมรับแล้ว

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

ตรวจรูปแบบ redirect. ใช้การตรวจรูปแบบกับที่อยู่ปัจจุบันแบบเดียวกับหน้าผู้ดูแล

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: redirects:write
  • body มี delegation_id, redirects
  • คำตอบ: ผลการตรวจรูปแบบและ digest ที่ต้องใช้ตอนบันทึก

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

แก้ redirect. รับเฉพาะการเปลี่ยน redirect ใช้การตรวจรีวิชัน path ชนกัน วงวน และการตรวจรูปแบบเดิม

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: redirects:write
  • ต้องมี Idempotency-Key body มี delegation_id, expected_revision, redirects และ redirect_review_digest ที่ไม่บังคับ
  • คำตอบ: เฉพาะ revision และ redirects ที่ยอมรับแล้ว

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

กู้รายการจากถังขยะ. กู้เป็นฉบับร่างผ่านการตรวจวงจรเนื้อหา ที่อยู่ สิทธิ์ฟิลด์และภาษา รวมถึงความเป็นเจ้าของเดิม

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: content:restore
  • ต้องมี Idempotency-Key body มี delegation_id
  • คำตอบ: ผลการกู้จากถังขยะตามปกติ

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

ลบสื่อที่ไม่ใช้งาน. ต้องมีสิทธิ์ media:delete ปัจจุบัน เนื้อหาปัจจุบัน ประวัติที่เก็บไว้ และการอ้างอิงที่เพิ่มพร้อมกันป้องกันการลบสื่อที่ยังใช้งาน การล้างไฟล์ในที่เก็บแยกจากการลบข้อมูล

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: media:delete
  • ต้องมี Idempotency-Key body มี delegation_id
  • คำตอบ: ผลการลบสื่อตามปกติและสถานะการล้างไฟล์

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

ส่งแม่แบบอีเมลที่อนุมัติ ต้องมีสิทธิ์ site:plugins ปัจจุบันและแม่แบบที่ผู้ดูแลตรวจแล้ว แบบระบุที่อยู่ใช้ delegation แบบ manage และไม่ส่ง instance_id สำหรับ entry_author หรือ entry_assignee ให้ส่ง instance_id ของรายการที่อยู่ในขอบเขตโมเดลและรายการของ installation และ delegation โดยผู้เริ่มส่งต้องยังอ่านรายการได้ ใช้ delegation แบบ manage หรือของการทำงานกับรายการที่ตรงกันได้ entry_author เลือกบัญชีที่สร้างรายการที่เลือกใน CMS (content_instance.created_by_id) ไม่ใช่ผู้เขียนบล็อกสาธารณะ โปรไฟล์ที่เชื่อมโยง หรือผู้แก้ไขล่าสุด ส่วน entry_assignee เลือกผู้ได้รับมอบหมายปัจจุบันในเวิร์กโฟลว์ของรายการนั้น (content_workflow.assigned_to_id) บัญชีผู้รับต้องเปิดใช้งาน มีสิทธิ์เข้าถึงเว็บไซต์ในปัจจุบัน และอ่านรายการได้ ผู้ดูแลระบบสูงสุดที่เปิดใช้งานอาจเข้าเงื่อนไขได้โดยไม่ต้องมีบทบาทแยกสำหรับเว็บไซต์นี้ หากไม่มีบัญชีที่เข้าเงื่อนไขจะได้ validation_failed

ปลั๊กอินระบุอีเมล ID บัญชี วิธีเลือกผู้รับ หัวเรื่อง ข้อความ หรือข้อมูลรับรองผู้ให้บริการเองไม่ได้ คำตอบไม่เปิดเผยที่อยู่ จำกัด body ที่ 16384 ไบต์ และค่าทดแทนที่ประกาศไม่เกิน 16 ค่า ค่าละ 1000 อักขระ หลังแทนค่า หัวเรื่องต้องมี 1–200 อักขระโดยไม่ขึ้นบรรทัด และข้อความไม่เกิน 12000 อักขระ หากเกินจะปฏิเสธก่อนเก็บบันทึกการเรียกซ้ำ

คำขอที่รับไว้จะเก็บข้อความและบัญชีผู้รับ การลองใหม่ด้วยคีย์เดิมใช้ข้อมูลนี้ แม้ผู้เขียน ผู้ได้รับมอบหมาย หรือแม่แบบเปลี่ยนไป ระบบตรวจสิทธิ์ผู้เริ่มส่งและผู้รับเดิมในการอ่านรายการ สถานะบัญชี และอีเมลที่ต้องยังตรงกัน หากเปลี่ยนรายการหรือค่าทดแทนโดยใช้คีย์เดิมจะถูกปฏิเสธ การลองใหม่ใช้ ID ข้อความเดิม และผลจากผู้ให้บริการที่ยังไม่ยืนยันยังลองใหม่ได้

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: mail:send
  • ต้องมี Idempotency-Key body คือ $defs/mail_command
  • คำตอบ: operation_applied หรือ idempotent_replay, {sent:true} ยืนยันเพียงว่าผู้ให้บริการรับแล้ว ไม่ใช่ว่าเข้ากล่องจดหมายแล้ว

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

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

  • ผู้เรียก: ปลั๊กอินแทนบุคคลที่มอบสิทธิ์
  • ต้องมี: renderer:metadata
  • ต้องมี Idempotency-Key body คือ $defs/renderer_metadata_command
  • คำตอบ: ผล content_saved ตามปกติ

Outcome:

  • operation_applied หรือผลการบันทึกหรือรีลีสที่ระบุไว้
  • capability_refused, requester_unauthorized, validation_failed, version_conflict, limit_exceeded, temporary_failure

คำสั่งและการปฏิเสธทุกครั้งตอบด้วย outcome envelope — รหัสสำหรับเครื่องและข้อความสำหรับมนุษย์ การอ่านที่สำเร็จ /launch/exchange และ /invocations/exchange ไม่ได้ตอบเช่นนั้น: พวกมันตอบ { "data": … } (และ "next" บนหน้าของ list) โดยไม่มี outcome การปฏิเสธสามแบบมี data ด้วย: validation_failed ระบุกฎ ($defs/validation_detail) draft_conflict มี draft ปัจจุบันใน data.current และ media_duplicate มีไฟล์ที่มีอยู่แล้วและจำนวนสำเนาของมัน ($defs/media_duplicate)

{
"outcome": {
"code": "transition_applied",
"message": "…",
"retry": false
},
"data": {}
}

แยกกรณีตาม code หลังตรวจ code, HTTP status, ค่า retry และ message ที่เป็นข้อความแล้ว ข้อความมีไว้สำหรับคนและไม่ใช่ค่าคงที่ ค่า retry เพียงอย่างเดียวไม่ได้ทำให้คำตอบเชื่อถือได้

HTTP รหัส Retry ความหมาย
200 release_applied ไม่ ใช้รีลีสแบบอะตอมมิกแล้ว
200 operation_applied ไม่ ใช้ operation แล้ว
200 transition_applied ไม่ transition ถูกนำไปใช้แล้ว
200 idempotent_replay ไม่ คำสั่งนี้ถูกนำไปใช้แล้ว ผลลัพธ์แรกถูกส่งกลับ
200 content_saved ไม่ ค่าฟิลด์ถูกบันทึกเป็นเวอร์ชันใหม่
200 content_deleted ไม่ ย้ายรายการไปถังขยะแล้ว
200 draft_saved ไม่ working draft ถูกบันทึกแล้ว
200 draft_discarded ไม่ working draft ถูกทิ้งแล้ว
200 media_uploaded ไม่ media ถูกอัปโหลดแล้ว
200 media_reused ไม่ ไซต์มีไฟล์นี้อยู่แล้ว ส่ง media ที่มีอยู่กลับไปและไม่ได้อัปโหลดอะไร
200 media_updated ไม่ อัปเดตข้อมูลสื่อแล้ว
200 proposal_created ไม่ ข้อเสนอถูกสร้างแล้ว ไม่มีอะไรเปลี่ยนจนกว่าบุคคลจะนำไปใช้
400 invalid_request ไม่ request ไม่ถูกต้องสำหรับ protocol v1
401 plugin_unauthorized ไม่ token ของ installation หายไป, ไม่รู้จัก, ถูก disable หรือถูก revoke
403 capability_refused ไม่ installation นี้ไม่ได้รับ grant capability หรือโมเดลนั้น
403 requester_unauthorized ไม่ บุคคลที่ร้องขอสิ่งนี้ไม่มีอำนาจที่ต้องใช้อีกต่อไป
404 content_not_found ไม่ ไม่มีเนื้อหานี้ในขอบเขตของ installation นี้
409 scheduled_target_stale ไม่ version ที่ถูก pin ไม่ใช่ตัวที่ transition นี้จะเคลื่อนย้ายอีกต่อไป
409 workflow_conflict ไม่ เนื้อหาเคลื่อนผ่าน workflow ของมันไปแล้วหลังจากที่มีการร้องขอนี้
409 dependent_content ไม่ มีเนื้อหาอื่นที่พึ่งพารายการนี้ บุคคลต้องตรวจทานการยกเลิกการเผยแพร่ใน Dee Wan
409 idempotency_key_reused ไม่ idempotency key นี้ถูกใช้ไปแล้วกับคำสั่งอื่น
409 version_conflict ไม่ รายการเปลี่ยนหลังเวอร์ชันที่ pin ไว้ อ่านใหม่ก่อนเขียน
409 media_duplicate ไม่ ไซต์มีไฟล์นี้อยู่แล้ว ไม่ได้อัปโหลดอะไร ส่ง on_duplicate เพื่อใช้ไฟล์นั้นหรืออัปโหลดสำเนา
409 draft_conflict ไม่ working draft เปลี่ยนหลัง revision ที่ pin ไว้ อ่านใหม่ก่อนเขียน
413 limit_exceeded ไม่ คำขอเกินขีดจำกัดขนาดหรือจำนวน
422 transition_unavailable ไม่ transition นั้นใช้ไม่ได้จาก state ปัจจุบันของเนื้อหา
422 transition_refused ไม่ lifecycle ปฏิเสธ transition นี้ ไม่มีอะไรถูกเปลี่ยน
422 validation_failed ไม่ กฎของเนื้อหาปฏิเสธค่าเหล่านี้ ไม่มีอะไรถูกเปลี่ยน
429 rate_limited ใช่ request มากเกินไปสำหรับ installation นี้
503 temporary_failure ใช่ Dee Wan ดำเนินการ request ไม่สำเร็จ ไม่มีสิ่งใดที่ทราบว่าถูกเปลี่ยน
  • ตรวจ structured outcome ก่อนใช้กฎสำรองตาม HTTP status ค่า code ที่รู้จัก HTTP status และ retry แบบ boolean ต้องตรงกับตาราง และ message ต้องเป็นข้อความ หากไม่ตรงหรือสมาชิก outcome ผิดรูปแบบ ให้จบด้วย untrusted_response แม้ status จะลองใหม่ได้
  • rate_limited (429) และ temporary_failure (503) ที่ถูกต้องให้ลองใหม่ได้ core ส่ง temporary_failure เฉพาะกับ 503 ไม่ส่งกับ 500, 502 หรือ 504
  • คำตอบขาเข้า 429/500/502/503/504 ที่ไม่มีสมาชิก outcome ให้ลองใหม่ได้ รวมถึง body ว่าง ไม่ใช่ JSON และข้อผิดพลาดจากตัวกลาง หากเป็น status อื่น body ของคำสั่งที่ไม่มี outcome ถูกต้องให้จบด้วย untrusted_response และไม่ตาม redirect
  • ข้อผิดพลาดเครือข่าย timeout และการอ่านคำตอบล้มเหลวให้ลองใหม่เป็น no_response สิ่งเหล่านี้ไม่ได้พิสูจน์ว่าไม่มีผลเกิดขึ้น token ที่เก็บไว้แต่อ่านไม่ได้ให้จบด้วย token_unreadable
  • เคารพ Retry-After บนคำตอบที่ลองใหม่ได้: วินาทีจำนวนเต็มไม่ติดลบหรือวันที่ HTTP มาตรฐาน (IMF-fixdate) วันที่ผ่านมาแล้วให้หน่วงศูนย์ ค่าผิดรูปแบบให้ใช้ exponential backoff แบบมีขอบเขตพร้อม jitter หากเวลารอเกินเพดาน หรือเกินจำนวนครั้งหรืออายุ ให้จบด้วย retry_exhausted
  • ความสำเร็จที่ถูกต้องคือ code 200: release_applied, operation_applied, transition_applied, idempotent_replay, content_saved, content_deleted, draft_saved, draft_discarded, media_uploaded, media_reused, media_updated, proposal_created outcome อื่นที่ถูกต้องแต่ลองใหม่ไม่ได้คือการปฏิเสธขั้นสุดท้ายด้วย code เดิม
  • ใช้ body และ Idempotency-Key เดิมทุกไบต์เมื่อ retry หรือกู้คืน lease คำขออาจซ้ำ แต่ผลต้องเกิดไม่เกินหนึ่งครั้งภายในช่วงเก็บ key เฉพาะผู้ครอง lease ปัจจุบันเท่านั้นที่เริ่มคำสั่งได้ และ deadline ของคำขอต้องก่อน lease หมดอายุ ห้ามตั้งใจส่ง delivery เดียวกันพร้อมกัน timeout ไม่ได้พิสูจน์ว่าคำขอระยะไกลหยุดแล้ว
  • กำหนดเพดานอายุให้น้อยกว่า 30 วันมาก เมื่อพ้นช่วงเก็บข้อมูล core จะลืม key เมื่อถึงขีดจำกัดการลองใหม่แล้ว ห้ามส่งคำขอเพิ่ม ผลที่ยังไม่ได้รับการยืนยันยังถือว่าไม่ทราบ ให้ตรวจเนื้อหาก่อนสร้างรายการกำหนดเวลาใหม่ด้วย key ใหม่
  • กฎนี้ใช้กับการ retry คำสั่ง การอ่านที่สำเร็จยังต้องมีรูปแบบข้อมูลตามเอกสาร /launch/exchange ใช้ code ได้ครั้งเดียวและไม่ควรลองซ้ำอัตโนมัติเมื่อคำตอบหาย

route ฝั่งเบราว์เซอร์เหล่านี้ต้องมี session ของผู้ใช้และ header เว็บไซต์ การติดตั้ง มอบสิทธิ์ เปลี่ยนโทเค็น ถอนการติดตั้ง และตั้งค่าเทมเพลตอีเมลต้องมี site:plugins ส่วนมุมมองที่มีชื่อ ใช้สิทธิ์โมเดลและสิทธิ์ระบบปัจจุบันที่ประกาศไว้แทน โทเค็นปลั๊กอินเรียก route เหล่านี้ไม่ได้ เอกสารส่วนนี้อธิบายให้ผู้เขียนปลั๊กอินเข้าใจอินเทอร์เฟซของโฮสต์ route ที่ทำ mutation แต่ละตัวจะเขียน audit entry และจะปฏิเสธ (503 audit_unavailable) แทนที่จะ mutate โดยไม่มี audit การปฏิเสธจะตอบ { "error": <text>, "reason": <code> } ให้แยกกรณีตาม reason ไม่ใช่ตาม error missing_permission ยังระบุสิทธิ์ที่ต้องใช้ไว้ใน required ด้วย

แสดงการทำงานที่ปลั๊กอินที่ติดตั้งประกาศไว้สำหรับกำหนดให้บทบาท รหัสการทำงานมี namespace ของปลั๊กอินกำกับ การทำงานที่ถูกลบหรือไม่มีคำประกาศไม่ให้สิทธิ์เรียกใช้งาน

  • ผู้เรียก: ผู้ใช้ที่มีสิทธิ์จัดการบทบาท
  • สิทธิ์ที่ต้องมี: role:manage
  • คำตอบ: { "data": [{ "plugin_id", "plugin_name", "action_id", "action", "label" }] }
  • รหัสคำตอบ: 200, 403

รายการพื้นที่ทำงานปลั๊กอินที่ใช้ได้. เมนู ตำแหน่งบนแดชบอร์ด และ route โดยตรงใช้การตรวจสิทธิ์ปัจจุบันเดียวกัน เฟรมแยกจาก origin ของหน้าผู้ดูแล

  • ผู้เรียก: ผู้ใช้ที่ลงชื่อเข้าใช้
  • ต้องมี: capability ที่ประกาศของมุมมองในปัจจุบัน และโมเดลที่ได้รับสิทธิ์และอ่านได้
  • คำตอบ: มุมมองที่มีชื่อและประกาศเฟรมที่ตรวจแล้ว

Outcome:

  • 200

เปิดพื้นที่ทำงานปลั๊กอินที่ใช้ได้. มุมมองที่ไม่มีให้ใช้ ถูกปิด ไม่ได้รับสิทธิ์ หรืออ่านไม่ได้ ตอบ not_found

  • ผู้เรียก: ผู้ใช้ที่ลงชื่อเข้าใช้
  • ต้องมี: สิทธิ์ปัจจุบันเดียวกับที่ใช้ในรายการ
  • คำตอบ: มุมมองที่ตรวจแล้วหนึ่งรายการ

Outcome:

  • 200
  • 404

ตรวจเทมเพลตอีเมลและผู้รับ. รับเฉพาะ ID เทมเพลตและค่าทดแทนที่ประกาศ การตั้งค่าที่อนุมัติเก็บแยกต่อ installation และบันทึกในการตรวจสอบ

  • ผู้เรียก: ผู้ดูแลเว็บไซต์
  • ต้องมี: site:plugins
  • map จาก ID แม่แบบที่ประกาศไปยัง {recipient_source?, recipients, subject, text}: ไม่เกิน 8 แม่แบบ หากไม่ระบุ recipient_source หรือใช้ fixed ต้องมีที่อยู่ที่อนุมัติ 1–8 ราย ส่วน entry_author และ entry_assignee ต้องไม่มีที่อยู่ (recipients: []) หัวเรื่อง 1–200 อักขระโดยไม่ขึ้นบรรทัด และข้อความ 1–12000 อักขระ
  • คำตอบ: การตั้งค่าเทมเพลตที่อนุมัติ

Outcome:

  • 200
  • 403
  • 409
  • 422

แสดงรายการ installation ทุก installation ที่ยังไม่ถูก revoke บนไซต์ แบบปกปิดข้อมูล: ไม่มี token ไม่มี hash

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • ไม่มี body
  • คำตอบ: { "data": [<installation>] } — $defs/installation

Outcome:

  • 200
  • 403 missing_permission

โมเดลที่ grant ได้ id, ชื่อ และ slug ของทุกโมเดลเนื้อหาบนไซต์ สำหรับหน้าจอ grant GRANT เก็บ id ดังนั้นการเปลี่ยนชื่อยังคงรักษามันไว้ และโมเดลที่ถูกลบจะทำให้ส่วนนั้นของมันไม่มีผล

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • ไม่มี body
  • คำตอบ: { "data": [{ "id", "name", "slug" }] }

Outcome:

  • 200
  • 403 missing_permission

อ่านแคตตาล็อกปลั๊กอิน ดึง URL แคตตาล็อกทุกตัวที่ผู้ดูแลระบบตั้งไว้ใน PLUGIN_CATALOGUE_URLS ภายใต้กฎ URL และการปฏิเสธการเปลี่ยนเส้นทางแบบเดียวกับ manifest ไม่มีอะไรถูกเก็บและไม่มีอะไรได้รับความไว้วางใจ: รายการให้เพียง URL ของ manifest และการติดตั้งจากรายการคือ Review และ Install ตามปกติ ซึ่งดึงและตรวจ manifest อีกครั้ง หน้าแคตตาล็อกคือ { "catalogue_version": 1, "plugins": [<entry>], "next"?: "https://…" } รายการคือ { "id", "name", "manifest_url", "summary"?, "categories"?, "homepage"?, "icon"? } โดยไม่มีคีย์อื่น id ใช้กฎเดียวกับ id ของ manifest name ยาวได้ไม่เกิน 64 ตัวอักษร summary ไม่เกิน 280 categories มี slug ตัวพิมพ์เล็กได้ไม่เกิน 5 ตัว URL ทุกตัวต้องผ่านกฎ URL ของปลั๊กอิน next ต้องอยู่บน origin ของหน้าแรก ขีดจำกัดต่อแคตตาล็อก: 5 หน้า หน้าละ 128 KiB หน้าละ 100 รายการ และรวม 200 รายการ อ่านได้สูงสุด 4 แคตตาล็อก แคตตาล็อกที่ผิดกฎถูกตัดออกทั้งหมดและระบุใน catalogues รายการที่ไม่ถูกต้องถูกข้ามและระบุใน problems เมื่อสองรายการมี id เดียวกัน รายการแรกชนะ: ตามลำดับแคตตาล็อกที่ตั้งค่า แล้วตามลำดับหน้า แล้วตามลำดับรายการ แคตตาล็อกที่ล้มเหลวไม่เปลี่ยนปลั๊กอินที่ติดตั้งแล้วหรือการติดตั้งโดยตรง

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • ไม่มี body
  • คำตอบ: { "data": { "configured", "entries": [{ "id", "name", "summary", "categories", "manifest_url", "homepage", "icon", "catalogue" }], "catalogues": [{ "catalogue", "origin", "ok", "entries" | "reason" }], "problems": [{ "catalogue", "index", "plugin_id", "reason", "fields" }] } }

Outcome:

  • 200 — รวมถึงเมื่อไม่ได้ตั้งค่าแคตตาล็อก (configured: false) และเมื่อทุกแคตตาล็อกล้มเหลว reason ของแคตตาล็อกคือ url_invalid, url_not_https, url_credentials, url_fragment, url_private_destination, remote_redirect_refused, remote_body_too_large, remote_unreachable, catalogue_unavailable, catalogue_invalid, catalogue_next_refused, catalogue_too_many_pages, catalogue_too_many_entries หรือ too_many_catalogues reason ของปัญหาคือ entry_invalid หรือ entry_duplicate
  • 403 missing_permission

action ของเนื้อหาที่จะ render คู่ { id, label } ที่มีขอบเขตซึ่ง installation ที่ active ประกาศไว้ เพื่อให้หน้าจอเนื้อหา render ปุ่มที่ core เป็นเจ้าของได้ ไม่มี markup, ไม่มี script, ไม่มี CSS, ไม่มี URL ของไอคอนใดมาจากปลั๊กอินเลย เมื่อมี instance_id จะแสดงเฉพาะ installation ที่ grant ของโมเดลรวมโมเดลของรายการนั้น และรายการที่ไม่รู้จักจะไม่แสดงอะไรเลย

  • ผู้เรียก: สมาชิกใดก็ได้ของไซต์
  • ต้องการ: ไม่มีอะไรนอกจากสิทธิ์เข้าถึงไซต์
  • instance_id (query ที่ไม่บังคับ) — รายการที่ปุ่มเหล่านี้มีไว้สำหรับ ไม่มี body
  • คำตอบ: { "data": [{ "installation_id", "plugin_name", "actions": [{ "id", "label" }] }] }

Outcome:

  • 200

การ์ดในแถบข้างที่จะ render การ์ด entry_sidebar หรือ page_sidebar ที่ installation ที่ active ประกาศไว้ใน ui_extensions core วาดการ์ดแต่ละใบเองจาก title และ description: ไม่มี markup, script, CSS, frame หรือ URL ใดมาจากปลั๊กอิน การ์ด launch ผ่าน route launch ด้านล่าง การ์ดของ content action แสดงเฉพาะสำหรับรายการที่โมเดลอยู่ใน grant การ์ดของหน้าจัดการแสดงสำหรับรายการแบบนั้น หรือสำหรับผู้ใช้ที่มี site:plugins รายการที่ไม่รู้จักจะไม่แสดงการ์ดเลย

  • ผู้เรียก: สมาชิกใดก็ได้ของไซต์
  • ต้องการ: ไม่มีอะไรนอกจากสิทธิ์เข้าถึงไซต์
  • placement (query) — entry_sidebar หรือ page_sidebar instance_id (query ที่ไม่บังคับ) — รายการบนหน้าจอ ไม่มี body
  • คำตอบ: { "data": [<plugin_card>] } — $defs/plugin_card

Outcome:

  • 200
  • 400 invalid_placement

การดำเนินการที่ผู้ใช้นี้เรียกใช้ได้ operations ที่การติดตั้งที่ใช้งานอยู่ประกาศไว้และผู้ใช้นี้เรียกใช้ได้ตอนนี้ ไม่มี instance_id: การดำเนินการระดับไซต์ (ต้องมี site:plugins) และการดำเนินการกับรายการในโมเดลที่ผู้ใช้อ่านได้ มี instance_id: การดำเนินการของรายการนั้น การดำเนินการที่สิทธิ์ขาด requires จะไม่แสดง การดำเนินการ automation และคอลัมน์ในรายการเนื้อหาก็ไม่แสดงเช่นกัน endpoint ไม่ถูกแสดงเลย

  • ผู้เรียก: สมาชิกคนใดก็ได้ของไซต์
  • ต้องมี: ไม่มีอะไรนอกจากการเข้าถึงไซต์
  • instance_id (query ไม่บังคับ) — รายการบนหน้าจอ ไม่มี body
  • ตอบ: { "data": [{ installation_id, plugin_id, plugin_name, operation }] }

ผลลัพธ์:

  • 200

เรียกใช้การดำเนินการ core ตรวจค่ากับข้อมูลเข้าที่ประกาศ บันทึกการเรียกใช้ POST ซองไปยัง endpoint ที่ประกาศ แล้วรอการแลกรหัสและคำตอบ คำตอบที่เสร็จสมบูรณ์ถูกตรวจกับ $defs/operation_result และคอมโพเนนต์ผลลัพธ์ที่ประกาศ และบันทึกการตรวจสอบด้วย digest การส่งซ้ำแบบตรงกันด้วย Idempotency-Key และ body เดิมจะคืนผลลัพธ์แรกพร้อม replayed: true

  • ผู้เรียก: ผู้ใช้ที่เห็นการดำเนินการนั้นในรายการ
  • ต้องมี: กฎของการดำเนินการเอง: site:plugins สำหรับระดับไซต์ สิทธิ์อ่านรายการสำหรับการดำเนินการกับรายการ สิทธิ์แก้ไขสำหรับที่ทำลายข้อมูล
  • Body — { "instance_id"?: string, "input"?: { … } } instance_id จำเป็นสำหรับการดำเนินการกับรายการและถูกปฏิเสธสำหรับระดับไซต์
  • Idempotency-Key — จำเป็น
  • ตอบ: { "data": { invocation_id, installation_id, plugin_id, plugin_name, operation, instance_id, outcome: "completed", result } }

ผลลัพธ์:

  • 400 idempotency_key_required, malformed_body, invalid_request, input_invalid (พร้อม issues), input_too_large
  • 404 operation_unavailable — ไม่รู้จัก เป็นการดำเนินการ automation เป็นคอลัมน์ในรายการเนื้อหา หรือไม่ได้เสนอให้ผู้ใช้นี้ตอนนี้
  • 409 idempotency_key_reused, invocation_in_progress, plugin_unavailable
  • 422 plugin_refused — refusal: { code, message } ของปลั๊กอินเอง
  • 502 plugin_unreachable, plugin_response_invalid
  • 503 audit_unavailable
  • 504 plugin_timeout

คอลัมน์ในรายการเนื้อหาที่ต้องวาด การดำเนินการ page ที่การติดตั้งที่ใช้งานอยู่ประกาศไว้สำหรับโมเดลหนึ่ง: scope: "page", audience: "column" และชนิด cell core เพิ่มคอลัมน์ที่มีหัวเป็นชื่อการดำเนินการในรายการเนื้อหาของโมเดลนั้น คอลัมน์แสดงเฉพาะเมื่อการติดตั้งใช้งานอยู่ โมเดลอยู่ในสิทธิ์ requires ของมันได้รับสิทธิ์ และผู้ใช้อ่านโมเดลได้ scope และรายการโมเดลของ credential จำกัดมันเช่นเดียวกับส่วนอื่นของ API ปลั๊กอิน

  • ผู้เรียก: สมาชิกของไซต์ที่อ่านโมเดลได้
  • ต้องมี: ไม่มีอะไรนอกจากการอ่านโมเดล
  • model_id (query) — จำเป็น ไม่มี body
  • ตอบ: { "data": [{ installation_id, plugin_name, operation, header, cell }] } cell คือ text, badge หรือ link

ผลลัพธ์:

  • 200
  • 400 model_id_required

คำนวณหนึ่งคอลัมน์สำหรับหนึ่งหน้าของแถว core เก็บแถวที่ผู้ใช้อ่านได้ตอนนี้ (ไม่เกิน 50 ในโมเดลที่ระบุ ไม่อยู่ในถังขยะ) และรันการดำเนินการ page ผ่านทรานสปอร์ตการเรียกใช้เดียวด้วยข้อมูลเข้า $defs/column_request: โมเดลและรหัสของแถวเหล่านั้น ไม่มีค่า ปลั๊กอินตอบบล็อก cells หนึ่งบล็อก มีไม่เกินหนึ่งเซลล์ต่อแถวที่ถูกถาม เซลล์ของแถวอื่น แถวซ้ำ ลิงก์ที่ไม่มี url แบบ https url หรือ tone บนชนิดที่ไม่รับ หรือข้อความเกิน 80 ตัวอักษร ทำให้คำตอบทั้งหมดเป็น plugin_response_invalid คอลัมน์เป็นการอ่าน: ไม่สร้างการมอบสิทธิ์ ไม่เก็บคำตอบที่เล่นซ้ำ และไม่เขียนแถวบันทึกการตรวจสอบ

  • ผู้เรียก: ผู้ใช้ที่อ่านโมเดลได้
  • ต้องมี: ไม่มีอะไรนอกจากการอ่านโมเดล
  • Body — { "model_id": string, "instance_ids": string[1..50] } ปิด
  • ตอบ: { "data": { installation_id, plugin_name, operation, header, cell, cells: [{ id, text, tone?, url? }] } } cells ว่างเมื่อผู้ใช้อ่านแถวใดไม่ได้เลยหรือปลั๊กอินไม่มีอะไรจะแสดง

ผลลัพธ์:

  • 400 malformed_body, invalid_request
  • 404 operation_unavailable — ไม่รู้จัก ไม่ใช่คอลัมน์ หรือไม่ได้เสนอให้ผู้ใช้นี้ตอนนี้
  • 409 plugin_unavailable
  • 422 plugin_refused
  • 502 plugin_unreachable, plugin_response_invalid
  • 504 plugin_timeout

การทำงานกับแถวที่เลือก การดำเนินการ entry แบบ interactive ที่มี list_action: true ซึ่งการติดตั้งที่ใช้งานอยู่ประกาศไว้สำหรับโมเดลหนึ่ง core เสนอมันกับแถวที่ผู้ใช้เลือกในรายการเนื้อหา อันที่ทำลายข้อมูลแสดงเฉพาะผู้ที่แก้ไขในโมเดลได้ endpoint ไม่ถูกแสดงเลย

  • ผู้เรียก: สมาชิกของไซต์ที่อ่านโมเดลได้
  • ต้องมี: ไม่มีอะไรนอกจากการอ่านโมเดล
  • model_id (query) — จำเป็น ไม่มี body
  • ตอบ: { "data": [{ installation_id, plugin_id, plugin_name, operation }] }

ผลลัพธ์:

  • 200
  • 400 model_id_required

รันการทำงานกับแถวที่เลือก core รันการดำเนินการหนึ่งครั้งต่อแต่ละแถวที่ระบุ ผ่านบริการเดียวกับ Run an operation ดังนั้นแต่ละแถวจึงถูกตรวจกับสิทธิ์ปัจจุบันของผู้ใช้ ตรวจกับข้อมูลเข้าที่ประกาศ บันทึกการตรวจสอบ และมีคำตอบของตัวเอง ไม่เกิน 25 แถวที่ต่างกัน รันครั้งละไม่กี่แถว การปฏิเสธแถวหนึ่งไม่หยุดแถวอื่น คีย์ idempotency ของแต่ละแถวมาจาก Idempotency-Key ของคำขอและรหัสแถว ดังนั้นการลองซ้ำทั้งคำขอจะเล่นซ้ำแถวที่รันไปแล้วและรันที่เหลือ

  • ผู้เรียก: ผู้ใช้ที่เห็นการดำเนินการนั้นในรายการ ทีละแถว
  • ต้องมี: กฎของการดำเนินการเองสำหรับแต่ละแถว: สิทธิ์อ่านรายการ สิทธิ์แก้ไขสำหรับที่ทำลายข้อมูล
  • Body — { "instance_ids": string[1..25], "input"?: { … } } ปิด input เดียวกันใช้กับทุกแถว
  • Idempotency-Key — จำเป็น
  • ตอบ: { "data": { "results": [<item>], "summary": { completed, failed } } } item คือ { instance_id, ok: true, run } โดย run คือการรันการดำเนินการของ Run an operation หรือ { instance_id, ok: false, status, reason, error, refusal? } พร้อมเหตุผลที่ route นั้นจะตอบสำหรับแถวนั้น

ผลลัพธ์:

  • 200 — ไม่ว่าผลของแต่ละแถวจะเป็นอย่างไร
  • 400 idempotency_key_required, malformed_body, invalid_request
  • 404 operation_unavailable — ไม่รู้จักหรือไม่ใช่การทำงานกับแถว

ตัวแก้ไขฟิลด์ที่จะ render installation ทุกตัวที่ active หรือถูกปิด พร้อม extension field_editor ที่ manifest ที่เก็บไว้ประกาศ core วาดตัวแก้ไขของปลั๊กอินเฉพาะสำหรับ installation ที่ active และเฉพาะเมื่อฟิลด์ยังเข้ากับมัน มิฉะนั้นฟิลด์ยังเก็บค่าไว้ และ core วาดตัวแก้ไขในตัวของตัวเอง พร้อมบอกชื่อปลั๊กอินและเหตุที่ตัวแก้ไขของมันใช้ไม่ได้ ฟิลด์ของโมเดลระบุตัวแก้ไขใน ui.pluginEditor ({ "plugin": "<plugin id>", "extension": "<extension id>" }) การบันทึกโมเดลที่เพิ่มหรือเปลี่ยนการอ้างอิงนี้จะถูกปฏิเสธ เว้นแต่ installation ที่ active ประกาศตัวแก้ไขนั้น และฟิลด์มีชนิดที่เก็บ กฎ และตัวเลือกของมัน (400 plugin_editor_not_installed, plugin_editor_not_active, plugin_editor_not_found, plugin_editor_type_mismatch, plugin_editor_rules_mismatch, plugin_editor_options_mismatch หรือ plugin_editor_invalid) การอ้างอิงที่ไม่เปลี่ยนยังคงอยู่หลังปลั๊กอินถูกปิดหรือถอนการติดตั้ง

  • ผู้เรียก: สมาชิกใดก็ได้ของไซต์
  • ต้องการ: ไม่มีอะไรนอกจากสิทธิ์เข้าถึงไซต์
  • ไม่มี body
  • คำตอบ: { "data": [<field_editor_source>] } — $defs/field_editor_source

Outcome:

  • 200

บล็อก rich text ที่จะเสนอ installation ทุกตัวที่ active หรือถูกปิด ซึ่ง manifest ที่เก็บไว้ประกาศ rich_blocks โดยแต่ละนิยามอยู่ใน namespace <plugin id>/<name> ตัวแก้ไข rich text เสนอบล็อกของ installation ที่ active ในเมนูแทรก วาดฟิลด์ของบล็อกเอง และแสดงพรีวิวจาก tree render ที่ประกาศ บล็อกที่เก็บไว้คงชนิด เวอร์ชัน และค่าเมื่อปลั๊กอินถูกปิดหรือถอนการติดตั้ง ตัวแก้ไขแสดงว่าเก็บไว้แต่ไม่ได้ลงทะเบียน และ renderer ไม่แสดงอะไรสำหรับบล็อกนั้นบนหน้าสาธารณะ การบันทึกเนื้อหาที่เพิ่มหรือเปลี่ยนบล็อกของปลั๊กอินจะถูกปฏิเสธ เว้นแต่ installation ที่ active ประกาศชนิดของบล็อก (422 rich_block_unregistered) รู้จักเวอร์ชัน (rich_block_version_unknown) และรับค่าหลัง migration ที่ประกาศไว้ (rich_block_values_invalid) บล็อกที่ไม่เปลี่ยนถูกเก็บไว้ไม่ว่าอะไรจะลงทะเบียนอยู่

  • ผู้เรียก: สมาชิกใดก็ได้ของไซต์
  • ต้องการ: ไม่มีอะไรนอกจากสิทธิ์เข้าถึงไซต์
  • ไม่มี body
  • คำตอบ: { "data": [<rich_block_source>] } — $defs/rich_block_source

Outcome:

  • 200

view บรรณาธิการที่บุคคลนี้เปิดได้ ทุก installation ที่ยังไม่ถูกถอนการติดตั้งและ manifest ที่เก็บไว้ประกาศ manage view (calendar, releases) พร้อม view ที่ประกาศและสถานะสำหรับบุคคลนี้: available (active และบุคคลนั้นอ่านโมเดลใน grant ได้อย่างน้อยหนึ่งโมเดล รวมถึงอ่านได้เฉพาะรายการของตัวเอง), no_readable_models (บุคคลนั้นอ่านโมเดลใน grant ไม่ได้เลยสักโมเดล), disabled หรือ unavailable (ยังไม่ active) รายการว่างหมายความว่าไม่ได้ติดตั้งปลั๊กอินแบบนี้ ไม่แสดงอะไรเกี่ยวกับ grant มากกว่านั้น

  • ผู้เรียก: สมาชิกใดก็ได้ของไซต์
  • ต้องการ: ไม่มีอะไรนอกจากสิทธิ์เข้าถึงไซต์
  • ไม่มี query และไม่มี body
  • คำตอบ: { "data": [{ "installation_id", "plugin_name", "views", "state" }] }

Outcome:

  • 200

ดึงและตรวจสอบ manifest ดึง URL ของ manifest ตรวจสอบมัน และคืนสิ่งที่มันขอ ไม่มีอะไรถูกเก็บและไม่มี installation เกิดขึ้นหลังจากนั้น นี่คือขั้นตอนตรวจทานที่ทำให้การอนุมัติเป็นไปอย่างมีข้อมูล

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "manifest_url": "https://…" }
  • คำตอบ: { "data": { "manifest": <manifest>, "protocol_version": 1, "review_digest": "<sha256>" } }

Outcome:

  • 200
  • 400 malformed_body / reason ของ manifest — manifest_invalid, protocol_unsupported, manifest_origin_mismatch, manifest_unavailable, url_invalid, url_not_https, url_credentials, url_fragment, url_private_destination, remote_redirect_refused, remote_body_too_large, remote_unreachable
  • 403 missing_permission

ติดตั้ง ดึงและตรวจสอบ manifest อีกครั้ง บันทึก installation สถานะ pending พร้อม capability และ id ของโมเดลที่อนุมัติ ออก token และส่ง token ดิบพร้อม activation code แบบใช้ครั้งเดียวไปยัง activation_url ของ manifest installation จะกลายเป็น active ก็ต่อเมื่อได้ 2xx จากปลั๊กอินเท่านั้น core เก็บ hash และไม่เคยเก็บ token activation ที่ล้มเหลวจะไม่ทิ้งอะไรไว้ และการติดตั้งปลั๊กอินเดียวกันเป็นครั้งที่สองบนไซต์เดียวกันคือ conflict ไม่ใช่ installation ที่สอง

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "manifest_url", "activation_code", "capabilities": […], "model_ids": […] } ฟิลด์ expected_review_digest ที่เลือกส่งได้จะตรึง URL และแมนิเฟสต์จากผลการตรวจ หากเปลี่ยนไป ระบบจะปฏิเสธก่อนส่งโทเคน capability ต้องเป็น subset ของคำขอใน manifest model_ids ที่ว่างหมายถึงไม่มีสิทธิ์เข้าถึงเนื้อหา ไม่ใช่ทุกโมเดล
  • คำตอบ: { "data": <installation> }, 201

Outcome:

  • 201
  • 400 malformed_body / invalid_activation_code / invalid_capabilities / invalid_model_ids / reason ของ manifest
  • 403 missing_permission
  • 409 installation_exists / backend_url_unset / activation_superseded / manifest_changed_since_review
  • 502 พร้อม reason — activation_refused (คำตอบที่ไม่ใช่ 2xx), url_invalid, url_not_https, url_credentials, url_fragment, url_private_destination, remote_redirect_refused, remote_body_too_large, remote_unreachable — ปลั๊กอินปฏิเสธหรือไม่สามารถติดต่อได้ ไม่มีอะไรถูกติดตั้ง
  • 503 audit_unavailable

เปลี่ยน grant แทนที่ capability และ id ของโมเดล ชุดสิทธิ์ที่กำหนดต้องอยู่ภายในสิ่งที่ manifest ที่ เก็บไว้ ขอ — capability ที่อยู่นอกนั้นจะถูกปฏิเสธ ดังนั้น manifest ที่เปลี่ยนไปไม่สามารถขยาย grant ได้ การลดสิทธิ์มีผลใน request ถัดไปของปลั๊กอิน

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "capabilities": […], "model_ids": […] }
  • คำตอบ: { "data": <installation> }

Outcome:

  • 200
  • 400 malformed_body / invalid_capabilities / invalid_model_ids
  • 403 missing_permission
  • 404 not_found
  • 409 installation_state_changed
  • 503 audit_unavailable

เลือกว่าเมื่อนโยบายเนื้อหาไม่ตอบจะเกิดอะไรขึ้น ตั้งนโยบายที่ประกาศไว้หนึ่งรายการเป็น warn (ค่าเริ่มต้น: หมดเวลา ระบบล่ม หรือคำตอบผิดรูปแบบ จะให้การเขียนผ่านพร้อมคำเตือน) หรือ required (ความล้มเหลวเดียวกันจะปฏิเสธการเขียน) refuse จริงจะปฏิเสธเสมอ expected_mode คือโหมดที่ผู้ดูแลเห็น การเปลี่ยนแปลงอื่นระหว่างนั้น หรือการอัปเกรดที่ยอมรับแล้ว ถือเป็นข้อขัดแย้ง

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "mode": "required", "expected_mode": "warn" }
  • คำตอบ: { "data": <installation> }

Outcome:

  • 200
  • 400 malformed_body
  • 403 missing_permission
  • 404 not_found / policy_not_found
  • 409 policy_mode_changed พร้อม mode ปัจจุบัน
  • 503 audit_unavailable

เปิดหรือปิด hook ก่อนการดำเนินการ ตั้ง hook ที่ประกาศไว้หนึ่งรายการเป็น on หรือ off hook ที่ประกาศไว้จะเป็น off จนกว่าผู้ดูแลจะเปิด ขณะเป็น on hook upload_metadata ถูกถามก่อนรับไฟล์ใหม่เข้าคลัง และ comment_moderation ก่อนเก็บความคิดเห็นของผู้เยี่ยมชม hook ที่ตอบไม่ได้จะปฏิเสธการอัปโหลดหรือปล่อยความคิดเห็นให้รอการตรวจ และไม่อนุมัติเด็ดขาด expected_mode คือสถานะที่ผู้ดูแลเห็น การเปลี่ยนแปลงอื่นระหว่างนั้น หรือการอัปเกรดที่ยอมรับแล้ว ถือเป็นข้อขัดแย้ง

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "mode": "on", "expected_mode": "off" }
  • คำตอบ: { "data": <installation> }

Outcome:

  • 200
  • 400 malformed_body
  • 403 missing_permission
  • 404 not_found / hook_not_found
  • 409 hook_mode_changed พร้อม mode ปัจจุบัน
  • 503 audit_unavailable

ยอมรับ manifest เวอร์ชันใหม่ ดึง URL ของ manifest ที่เก็บไว้อีกครั้งและยอมรับ release ที่ผู้ดูแลเพิ่งตรวจทาน — expected_version ต้องตรงกับสิ่งที่ถูก serve มิฉะนั้นการตรวจทานถือว่าเก่าแล้ว id ของปลั๊กอิน, origin และเวอร์ชัน protocol ที่ยอมรับต้องไม่เปลี่ยน ระบบจะเก็บเฉพาะ capability ที่ได้รับสิทธิ์อยู่แล้วและยังอยู่ในคำขอใหม่ การอัปเกรดไม่เคยขยาย grant release ที่เปลี่ยนเวอร์ชัน protocol ที่ยอมรับไม่สามารถถูกยอมรับได้: ให้ถอนการติดตั้งแล้วติดตั้งใหม่ นโยบายที่ release ใหม่ไม่ได้ประกาศแล้วจะเสียโหมดของตน นโยบายใหม่เริ่มที่ warn

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "expected_version": "0.2.0" }
  • คำตอบ: { "data": <installation> }

Outcome:

  • 200
  • 400 malformed_body / reason ของ manifest
  • 403 missing_permission
  • 404 not_found
  • 409 manifest_identity_changed / protocol_changed / manifest_changed_since_review / installation_state_changed
  • 503 audit_unavailable

Rotate token ต้องใช้รหัสใช้ครั้งเดียวใหม่จากปลั๊กอิน เพราะการ rotate คือการส่งมอบ ไม่ใช่การรีเซ็ต core ออก token ส่งไปยัง activation origin ที่ถูก pin และหลังจากนั้นจึงสลับ hash หากปลั๊กอินปฏิเสธ token เก่ายังใช้งานได้ hash ก่อนหน้ายังใช้ได้ไม่เกิน 10 นาที และ request แรกที่แนบ token ใหม่ จะยุติช่วงซ้อนทับนั้นทันที: ตั้งแต่นั้น token เก่าจะตอบ 401 plugin_unauthorized ปลั๊กอินที่มีหลาย worker ต้องสลับทั้งหมดพร้อมกัน หรือให้ worker อ่าน token ที่เก็บไว้ซ้ำหนึ่งครั้งเมื่อได้ 401 ทันทีหลังการ rotate

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • Body — { "activation_code": "…" }
  • คำตอบ: { "data": { "rotated": true } }

Outcome:

  • 200
  • 400 invalid_activation_code (รวมถึง body ที่ไม่ใช่ JSON object ด้วย)
  • 403 missing_permission
  • 404 not_found
  • 409 installation_not_active / backend_url_unset / installation_state_changed
  • 502 พร้อม reason — activation_refused (คำตอบที่ไม่ใช่ 2xx), url_invalid, url_not_https, url_credentials, url_fragment, url_private_destination, remote_redirect_refused, remote_body_too_large, remote_unreachable — และ token เก่า ยังใช้งานได้
  • 503 audit_unavailable

Disable เก็บการตั้งค่าและ audit trail ไว้ และหยุด plugin API ทันที: ทุก request ตอบ plugin_unauthorized ก่อนการเข้าถึง tenant ใด ๆ การ์ดของมันหายไป และฟิลด์ที่ระบุตัวแก้ไขฟิลด์ของมันยังเก็บค่าไว้และถูกวาดด้วยตัวแก้ไขในตัว

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • ไม่มี body
  • คำตอบ: { "data": { "id", "state": "disabled" } }

Outcome:

  • 200
  • 403 missing_permission
  • 404 not_found
  • 409 installation_state_changed
  • 503 audit_unavailable

Enable คืน installation ที่ถูก disable กลับเป็น active ด้วย token เดิมและ grant เดิม

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • ไม่มี body
  • คำตอบ: { "data": { "id", "state": "active" } }

Outcome:

  • 200
  • 403 missing_permission
  • 404 not_found
  • 409 installation_state_changed
  • 503 audit_unavailable

ถอนการติดตั้ง revoke ก่อน: state กลายเป็น revoked, hash ทั้งสองถูกทิ้ง และ token จะไม่กลับมาใช้ได้อีก การระบุที่มาใน audit ยังคงอยู่ เพราะ workflow event เก็บ snapshot ของ installation แทนที่จะเป็น foreign key Dee Wan ไม่ อ้างว่าการนี้ลบสิ่งใดที่ปลั๊กอินถืออยู่ — การลบฝั่ง remote เป็น action ของปลั๊กอินเอง การถอนการติดตั้งทำได้แม้ฟิลด์ของโมเดลยังระบุตัวแก้ไขฟิลด์ของมัน: ฟิลด์เหล่านั้นยังเก็บค่าและกฎไว้และถูกวาดด้วยตัวแก้ไขในตัว

  • ผู้เรียก: ผู้ใช้ที่มี site:plugins
  • ต้องการ: site:plugins
  • ไม่มี body
  • คำตอบ: { "data": { "id", "state": "revoked" } }

Outcome:

  • 200
  • 403 missing_permission
  • 404 not_found
  • 409 installation_state_changed
  • 503 audit_unavailable

เปิดปลั๊กอินแบบ delegate ออกรหัสใช้ครั้งเดียว มีอายุ 60 วินาที เก็บแบบ hash ผูกกับ installation, ไซต์, ผู้ใช้, action และ instance (ไม่บังคับ) และคืน management_url พร้อมรหัสนั้น response มี Referrer-Policy: no-referrer และ Cache-Control: no-store action ต้องเป็น manage โดยไม่มี instance หรือเป็น id ของ content_actions จาก manifest ที่เก็บไว้พร้อม instance หนึ่งตัว สำหรับ action ของเนื้อหา โมเดลของรายการต้องอยู่ใน grant ของ installation launch แบบ manage อาจระบุ view จาก manage_views ของ manifest ที่เก็บไว้ จากนั้น URL จะมี dee_wan_view ด้วย ซึ่งเป็นคำใบ้หน้าปลายทางที่ไม่ได้ให้สิทธิ์ใดๆ launch ที่มี view เป็นงานบรรณาธิการ: ไม่ต้องมี site:plugins ต้องมีเพียงโมเดลหนึ่งใน grant ที่บุคคลนั้นอ่านได้ ทุกรายการหรือเฉพาะรายการของตัวเอง launch แบบ manage ธรรมดาเป็นการดูแลปลั๊กอินและต้องมี site:plugins delegation บันทึก model_ids: โมเดลใน grant ที่บุคคลนั้นอ่านได้ (สำหรับ action ของเนื้อหา คือโมเดลของรายการนั้น) จำกัดตามโมเดลของ credential ของ management เมื่อ credential นั้นเป็นผู้กระทำ launch ที่ credential ของ management สร้างยังบันทึกเพดาน capability: capability ของ installation ที่ scopes ของ credential อนุญาต คำสั่งทุกครั้งหลังจากนั้นผ่าน delegation นี้ที่อยู่นอกเพดานจะถูกปฏิเสธด้วย capability_refused cookie ของ session Dee Wan ไม่เคยออกจาก Dee Wan

การเปิดมุมมอง manage ที่ประกาศไว้ส่ง entry_context: { instance_id, locale? } ได้ ระบบตรวจสิทธิ์อ่านปัจจุบัน สิทธิ์ของภาษา และขอบเขตที่อนุญาตของการติดตั้งและโมเดล ก่อนเพิ่ม dee_wan_entry และ dee_wan_locale เมื่อระบุภาษา ข้อมูลนี้เป็นเพียงคำแนะนำหน้าปลายทาง ไม่ให้สิทธิ์เพิ่ม การมอบสิทธิ์ของมุมมองยังไม่ผูกกับรายการใดรายการหนึ่ง และปลั๊กอินต้องอ่านรายการนั้นใหม่ด้วยสิทธิ์ปัจจุบัน

  • ผู้เรียก: สมาชิกของไซต์สำหรับ content action หรือ view ของ manage site:plugins สำหรับ manage ธรรมดา
  • ต้องการ: สิทธิ์อ่านรายการที่ระบุ สำหรับ content action และโมเดลใน grant ที่อ่านได้หนึ่งโมเดล สำหรับ view
  • Body — { "action": "manage" }, { "action": "manage", "view": "calendar" } (หรือ "releases") หรือ { "action": "<action id>", "instance_id": "…" }
  • มุมมองที่ประกาศไว้เพิ่ม "entry_context": { "instance_id": "…", "locale": "en" } ได้ โดยไม่จำเป็นต้องระบุ locale
  • คำตอบ: { "data": { "url": "https://…?dee_wan_launch=…" } } — $defs/launch

Outcome:

  • 200
  • 400 malformed_body / invalid_launch_action / invalid_entry_context
  • 403 missing_permission (สำหรับ manage ธรรมดา) / no_readable_models (launch ที่มี view และบุคคลนั้นอ่านโมเดลใน grant ไม่ได้เลยสักโมเดล) / model_denied / not_owner (บุคคลนั้นอ่านรายการไม่ได้) / model_not_granted (โมเดลของรายการอยู่นอก grant)
  • 404 not_found — ไม่มี installation นั้น หรือไม่มีรายการนั้น
  • 409 installation_not_active

ส่วนขยาย field_editor ที่มี control: "group" วาดฟิลด์ JSON หนึ่งฟิลด์เป็นสมาชิกที่มีป้ายชื่อไม่เกิน 8 ตัว: text (max_length ไม่เกิน 500), number (min, max, integer), boolean และ select (ตัวเลือกไม่เกิน 12) คีย์ของสมาชิกไม่ซ้ำ ค่าที่เก็บยังเป็นอ็อบเจ็กต์ JSON หนึ่งตัวไม่เกิน 8192 ไบต์ การเลือกตัวแก้ไขในตัวสร้างโมเดลจะเขียนสคีมาแบบปิดของมันลงในฟิลด์ (config.schema พร้อม validationType: "object") และการบันทึกปกติตรวจค่าที่ส่งมาและโครงสร้าง โดยปฏิเสธค่านอกขอบเขตหรือตัวเลือก คีย์ที่ไม่ใช่สมาชิก และค่าที่ไม่ใช่อ็อบเจ็กต์ โหมดฉบับร่างอนุญาตให้ยังไม่กรอกสมาชิกบังคับได้ แต่การบันทึกแบบเข้มงวดและการเผยแพร่ทุกครั้งต้องกรอกให้ครบ สคีมายังอยู่ในฟิลด์เมื่อปลั๊กอินถูกปิดหรือถอนการติดตั้ง ค่าที่เก็บจึงยังใช้ได้ และตัวแก้ไข JSON ในตัววาดค่าเหล่านั้น

การอ่านข้อมูลสรุปเดิมยังทำงาน เพิ่ม delegation_id ใน GET /content/:instanceId เพื่อรับ saved: { id, version_number, data } จำกัดข้อมูลตามสิทธิ์โมเดล ฟิลด์ ภาษา และเจ้าของปัจจุบัน ส่วน data เก็บค่าของ snapshot มาตรฐาน ความสัมพันธ์ และสื่อ ไม่มีการมอบสิทธิ์: ได้ข้อมูลสรุปเท่านั้น GET /content/:instanceId/versions?delegation_id=…&limit=50&cursor=… แสดงเวอร์ชันที่ยังเก็บอยู่ GET /content/:instanceId/versions/:versionId?delegation_id=… อ่าน snapshot ตามสิทธิ์ การอ่านประวัติตรวจความเป็นเจ้าของรายการหลัก เวอร์ชันที่ถูกลบตามระยะเก็บรักษาจะตอบ content_not_found

POST /content สร้างรายการผ่าน lifecycle ต้องมี content:create, การมอบสิทธิ์ manage และ Idempotency-Key ส่ง delegation_id, model_id, locales ตัวเลือก: primary_lang, slugs, unset_fields, relations, locale_relations, media, seo, label ใช้ unset_fields ระบุฟิลด์แยกตามภาษาที่ต้องการลบค่าที่เขียนทับ ฟิลด์ต้องแปลได้ ไม่บังคับกรอก และเปิดใช้ค่าจากภาษาอื่นเมื่อไม่มีคำแปล ห้ามกำหนดค่าและลบค่าในฟิลด์เดียวกันพร้อมกัน ใช้รูปแบบเดียวกับ management API วงจรเนื้อหาตรวจสิทธิ์สร้าง โมเดล ฟิลด์ ภาษา และเป้าหมายที่อ้างถึงทุกรายการ และตอบ content_saved PUT /content/:instanceId รับ relations, locale_relations, media เพิ่มจาก fields เดิม ต้องระบุ expected_version_id และเว้น fields ว่างได้เมื่อเปลี่ยนข้อมูลอ้างอิง ยังคงรับ locale และ delegation_id เดิม รูปแบบความสัมพันธ์และสื่อเหมือนการเขียนผ่าน API การจัดการ และใช้การตรวจวงจรเนื้อหา การตรวจเวอร์ชันก่อนเขียน และ idempotency ชุดเดียวกัน DELETE /content/:instanceId ย้ายทั้งรายการไปถังขยะ ส่ง delegation_id และ expected_version_id ต้องมี Idempotency-Key, content:delete และสิทธิ์ลบของผู้ใช้ ถอนการเผยแพร่รายการที่ออนไลน์ก่อน ตอบ content_deleted การลองซ้ำคืนผลเดิม การตรวจเวอร์ชันในการเขียนแบบอะตอมมิกป้องกันข้อมูลสูญหายจากการแก้ไขพร้อมกัน

GET /media/:mediaId?delegation_id=… อ่านข้อมูลไฟล์ /bytes อ่านไบต์จากผู้ให้บริการ ต้องมี media:read และสิทธิ์ media-library read ของผู้ใช้ แยกตามไซต์ รวมไฟล์ส่วนตัวและห้าม cache คำตอบที่มีไบต์ของไฟล์ส่วนตัวไม่ให้แคช และกำหนดวิธีเปิดหรือดาวน์โหลดไฟล์อย่างปลอดภัย ไฟล์อ้างอิงภายนอกไม่มีไบต์ในระบบ PUT /media/:mediaId ส่ง {delegation_id,metadata}, Idempotency-Key ต้องมี media:update รองรับ alt_texts, default_focal, loading, fetch_priority ตอบ media_updated และ {id} รูปแบบเมทาดาทาเหมือน API การจัดการ และบริการสื่อร่วมจะตรวจการจัดเฟรมด้วย

การ์ดและตัวแก้ไขฟิลด์ประกาศ frame: {url,origin,operations?} ได้ origin ต้องตรงกับ origin ของ URL ของเฟรมและ origin ของปลั๊กอินทุกตัวอักษร การดำเนินการต้องเป็นรายการที่ manifest เดียวกันประกาศไว้ ผู้ดูแลตรวจสอบก่อนยอมรับ Iframe ใช้ credentialless, sandbox="allow-scripts" และ no-referrer ไม่มี cookies ผู้ดูแลหรือ token เมื่อโหลด host ส่ง dee-wan:connect, protocol 1, value, disabled พร้อม MessagePort โฮสต์ส่ง {type:"value",value,disabled} ปัจจุบันกลับหลังอัปเดต และจำกัดความสูงที่เฟรมขอ ลูกใช้ port เท่านั้น ส่ง {type:"ready"}, {type:"change",value}, {type:"resize",height} หรือ {type:"operation",id,operation,input} ได้ Host ตรวจสิทธิ์และคำประกาศ เรียก operation เดิม ตอบ {type:"result",id,result} id ซ้ำคืนผลเดิม การแก้ไขฟิลด์ผ่านการบันทึกปกติ สิทธิ์ใหม่หลังอัปเกรดยังไม่ได้รับอนุญาต ไม่มี endpoint สำหรับ fetch ตามอำเภอใจ โทเค็นการติดตั้ง หรือรหัส launch ถูกส่งเข้าเฟรม

relation ระบุ models และ multiple ได้ group ระบุ fields, repeatable, max_items ได้ ซ้อนกลุ่มได้สามระดับ ไม่เกินห้าสิบรายการ ใช้ฟิลด์แบบเดิมและตัวเลือกความสัมพันธ์ Lifecycle ตรวจไซต์ โมเดล และสิทธิ์อ่านเป้าหมาย โครงสร้างการเรนเดอร์ใช้ {group,children} หรือ {each,children} เพื่อกำหนดขอบเขตการเชื่อมโยงข้อมูลของส่วนลูก สื่อระดับบนและภายในกลุ่มใช้ pin เดิม ย่อหน้าและหัวข้อรองรับ left, center, right, justify การจัดแนวเก็บด้วย data-align และสไตล์ text-align แบบมาตรฐาน ใช้สัญญาเดียวกันตั้งแต่ตัวแก้ไขจนถึงหน้าเว็บ

snippet เก็บบล็อกข้อความจัดรูปแบบชนิด snippet โดยมี html/css/javascript/title ส่วน HTML มาตรฐานเก็บ JSON ใน figure ที่ยังไม่ทำงาน พรีวิวในหน้าผู้ดูแลและตัวเรนเดอร์ที่รองรับใช้ endpoint คงที่เฉพาะสำหรับเฟรม และส่งข้อมูลผ่าน MessageChannel ในรูปแบบเดียวกัน ตัวเชื่อมในเอกสารแม่ของหน้าสาธารณะได้รับอนุญาตด้วยแฮช CSP ของเฟรมบังคับให้ทำงานใน sandbox ที่มี origin แบบ opaque โค้ดของผู้ให้บริการจึงอยู่ภายในเฟรม ทั้งสองใช้ข้อมูลเอกสารเดียวกันแบบ credentialless โดยไม่มีสิทธิ์ same-origin สคริปต์ ฟอร์ม และ popup ทำงานภายในเฟรมเท่านั้น ผู้ให้บริการเป็นผู้จัดการข้อมูลการจองหรือฟอร์มที่ส่ง ห้ามเก็บข้อมูลรับรองของผู้ให้บริการในเนื้อหา snippet

ข้อมูลที่บันทึกเก็บ ID ผลลัพธ์สาธารณะแทนเป้าหมายที่อ่านได้ด้วยออบเจ็กต์สำหรับอ่านที่มีขอบเขต: reference: true, instance_id, title, url ปัจจุบัน และ properties เฉพาะฟิลด์ scalar ที่เปิดเผยและถูกระบุด้วย property เช่น { field: "target", property: "address" } ส่วน { field: "target" } แสดงชื่อ ลิงก์ใช้ที่อยู่ Page/Blog/managed route ที่เผยแพร่จากตัวแก้ร่วม หรือฟิลด์ url สาธารณะ หากไม่มีที่อยู่จะเป็นข้อความ เป้าหมายที่ซ่อนและฟิลด์ส่วนตัวถูกตัดออก การบันทึกยังรับเฉพาะ ID โดยไม่ยอมรับชื่อและ URL ที่ผู้เรียกส่งมาเป็นสิทธิ์

  • schemas/manifest-v1.schema.json — manifest ที่สร้างจากออบเจกต์ ที่ core ใช้ตรวจสอบทุก manifest ที่ดึงมา
  • schemas/protocol-v1.schema.json — request, response, body ของการส่งมอบตอน activation, outcome, ขีดจำกัด และรูปแบบของ secret ที่สร้างจากนิยาม ชุดเดียวกัน shape ของ request เป็นแบบปิด shape ของ response อนุญาตให้มี property เพิ่มเติม เพราะ client ต้องเพิกเฉยต่อ key ใน response ที่ไม่รู้จัก

JSON Schema ไม่ใช่ตัวตรวจความหมายทั้งหมด ต้องเปิดการตรวจ format โดย URL ใช้ uri และ URL จัดการใช้ uri-template URL ของ manifest, base, activation และ management หลังแทนค่าต้องมี origin เดียวกันและผ่านนโยบาย HTTPS/ปลายทางส่วนตัว ยกเว้นเฉพาะ origin สำหรับพัฒนาที่ระบุอย่างเจาะจง การตรวจนี้ไม่ได้ resolve DNS capability ต้องไม่ซ้ำและ id ของ content action ต้องไม่ซ้ำด้วย id ของ ui_extensions ต้องไม่ซ้ำและไม่อยู่ใน reserved_ids และการ์ด launch ได้เฉพาะ id ใน content_actions หรือรายการใน manage_views ที่ manifest เดียวกันประกาศ ต้องมี protocol version ที่รองรับอย่างน้อยหนึ่งค่า grant ที่อนุมัติต้องไม่เกินที่ manifest ขอและโมเดลของไซต์นี้ kind ของ transition กำหนด version pin ที่ต้องมีเพียงตัวเดียว ส่วน status/code/retry ต้องตรงกันหลังตรวจโครงสร้าง


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