Providers

เชื่อมต่อโทรศัพท์ 3CX · DTAC

Providers

ข้อมูล credential ของแหล่งดึงสายเข้าระบบ — ช่องทางที่สายไหลเข้ามา มีสามชนิดในตัว: threecx (3CX PBX), dtac (DTAC carrier) และ minio (bucket แบบ S3 ที่ตู้สาขาของคุณเขียนไฟล์ลงไปอยู่แล้ว) ระบบอื่น ๆ ทีมของเราเพิ่มเป็น ตัวดึงเสียง (connector) ได้ โดยฝั่งคุณไม่ต้องแก้อะไร GET /providers/catalog บอกรายชื่อตัวดึงเสียงทุกตัวที่ธุรกิจของคุณใช้ได้ พร้อมช่องตั้งค่าที่แต่ละตัวต้องการ

ถ้าเป็นระบบโทรศัพท์ เราจะล็อกอินเข้าไปดึงสาย ส่วน object storage ไม่มีการล็อกอินและไม่มีรายชื่อพนักงาน — ระบบจะไล่ดู bucket ตามรอบแล้วดึงไฟล์เสียงที่เจอเข้ามา ทุกอย่างในหน้านี้ใช้ได้กับตัวดึงเสียงทุกตัว ตรงไหนที่ต่างกันจะระบุไว้

Account key: ทุก request ด้านล่างต้องส่ง X-Business-Id: <business id> เพื่อเลือกธุรกิจที่จะทำงานด้วย (ถ้าไม่ส่งจะได้ 400 business_required) ส่วน business key จะไม่อ่าน header นี้ เพราะผูกกับธุรกิจเดียวอยู่แล้ว ดูเพิ่มที่ เริ่มต้นใช้งาน → API key ระดับบัญชี

Secret เป็นแบบ write-only provider เก็บ credential ไว้ — password สำหรับชนิดโทรศัพท์ secretKey สำหรับ bucket และช่องใดก็ตามที่ตัวดึงเสียงประกาศว่าเป็น secret คุณส่งค่าพวกนี้ตอน create/update ได้ แต่ GET จะไม่คืนกลับมา — response ของการอ่านจะแสดงเฉพาะส่วนที่ไม่ลับ ส่วน list endpoint จะแสดง preview ที่ถูกปิดบังแทน ตอนแก้ไข ถ้าส่ง secret มาว่าง ระบบจะใช้ค่าเดิมที่เก็บไว้

GET /providers/catalog

รายชื่อตัวดึงเสียงที่ธุรกิจนี้เชื่อมต่อได้ — คือค่าที่ POST /providers รับเป็น kind — พร้อมช่องตั้งค่าที่แต่ละตัวต้องการ

Scopes: manage_provider_config

curl https://phone.mcloud.co.th/api/v1/providers/catalog \
  -H "Authorization: Bearer crk_..."
{
  "items": [
    {
      "slug": "threecx",
      "name": { "th": "3CX", "en": "3CX" },
      "description": { "th": "ระบบโทรศัพท์ 3CX — …", "en": "…" },
      "category": "phone",
      "fields": [
        { "key": "baseUrl", "type": "url", "label": { "th": "Base URL", "en": "Base URL" }, "help": null, "placeholder": "https://example.3cx.cloud", "required": true, "default": null, "min": null, "max": null, "options": null },
        { "key": "username", "type": "text", "label": { "th": "Username", "en": "Username" }, "help": null, "placeholder": null, "required": true, "default": null, "min": null, "max": null, "options": null },
        { "key": "password", "type": "secret", "label": { "th": "Password", "en": "Password" }, "help": null, "placeholder": null, "required": true, "default": null, "min": null, "max": null, "options": null }
      ],
      "features": { "people": true, "agentFilter": true, "objectFilter": false, "range": true, "preview": true },
      "schedule": { "defaultMinutes": 5, "minMinutes": 5, "maxMinutes": 1440, "rangeMaxDays": 30 },
      "available": true
    }
  ]
}
Fieldความหมาย
slugส่งค่านี้เป็น kind ของ POST /providers
categoryphone (ระบบโทรศัพท์/ระบบบันทึกเสียง) หรือ storage (bucket)
fields[]key ที่ config รับ type: text · url (เฉพาะ http/https) · secret (write-only) · number · boolean · select (ค่าหนึ่งใน options[].value) · list (array ของ string) — min/max คือขอบเขตของตัวเลข ความยาวของข้อความ หรือจำนวนรายการของ list ส่วน default ใช้เมื่อไม่ส่ง key นั้นหรือส่งมาว่าง
featurespeople → ใช้ GET /providers/{id}/people ได้ · agentFilter → รับ config.agentFilter · objectFilter → รับ config.objectFilter · range / preview → ดึงตามช่วงวันที่ และดูตัวอย่างสายที่จะดึงในหน้าเว็บได้
scheduleconfig.pollIntervalMinutes ต้องอยู่ระหว่าง minMinutes–maxMinutes ถ้าไม่ส่งจะใช้ defaultMinutes ถ้าตั้ง config.pollTimes (เวลาดึงประจำวัน) ไว้ จะใช้เวลานั้นแทนรอบดึง
availablefalse = ธุรกิจของคุณเพิ่มตัวดึงเสียงนี้ใหม่ไม่ได้แล้ว (ถูกปิด หรือไม่ได้เปิดให้แล้ว) แต่ยังมีการเชื่อมต่อเดิมอยู่

ชื่อและคำอธิบายเป็น { th, en } (มี ja / ko / zh-Hans เมื่อตัวดึงเสียงนั้นใส่มา) ตัวดึงเสียงบางตัวเปิดให้เฉพาะบางธุรกิจ — ตัวที่ธุรกิจของคุณใช้ไม่ได้จะไม่ปรากฏในรายการเลย

GET /providers

ดูแหล่งดึงสายที่ตั้งค่าไว้สำหรับธุรกิจนี้

Scopes: manage_provider_config

curl https://phone.mcloud.co.th/api/v1/providers \
  -H "Authorization: Bearer crk_..."
{
  "items": [
    {
      "id": "0192f0...",
      "kind": "threecx",
      "connector": "threecx",
      "name": "Main PBX",
      "isActive": true,
      "preview": "apiuser@https://pbx.example.com",
      "createdAt": "2026-05-01T00:00:00Z"
    },
    {
      "id": "0192f1...",
      "kind": "minio",
      "connector": "minio",
      "name": "PBX drop folder",
      "isActive": true,
      "preview": "recordings/pbx/2026/ @ https://s3.example.com:9000",
      "createdAt": "2026-08-01T00:00:00Z"
    }
  ]
}

kind คือชนิดที่เก็บไว้: threecx, dtac, minio หรือ script สำหรับตัวดึงเสียงทุกตัวที่เพิ่มภายหลัง ให้อ่านจาก connector — คือ slug ของตัวดึงเสียง ซึ่งเท่ากับ kind สำหรับสามชนิดในตัว

preview คือคำใบ้ที่ปิดบังค่าลับไว้: ระบบโทรศัพท์ในตัวเป็น username@baseUrl · bucket เป็น bucket/prefix @ endpoint · ตัวดึงเสียงอื่นเป็นค่าของช่องสรุปที่ตัวดึงเสียงกำหนด (คั่นด้วย ·) — ไม่มีค่าลับอยู่ในนั้น

POST /providers

สร้าง provider — config คือช่องตั้งค่าของตัวดึงเสียงนั้น (ดูด้านล่าง)

Scopes: manage_provider_config

ParamTypeNote
kind"threecx" | "dtac" | "minio" | slug ของตัวดึงเสียงrequired — slug ใดก็ได้จาก GET /providers/catalog ที่ available: true
namestring (2-60)required — ห้ามซ้ำกับ provider อื่นของธุรกิจ
configobjectrequired — โครงสร้างขึ้นกับตัวดึงเสียง
isActivebooleanoptional (default true)
{ "id": "0192f0...", "connector": "threecx" }
ErrorStatusเมื่อไหร่
connector_not_available403kind เป็นตัวดึงเสียงที่ธุรกิจของคุณใช้ไม่ได้ — ไม่มีอยู่จริง ถูกปิด หรือไม่ได้เปิดให้คุณ (ทั้งสามกรณีตอบเหมือนกัน)
provider_name_taken409มี provider อื่นใช้ name นี้แล้ว
validation_failed422config ไม่ตรงกับช่องตั้งค่าของตัวดึงเสียง — issues บอกว่าช่องไหน

config ของตัวดึงเสียงใดก็ได้: key ตาม fields ใน catalog บวก key สงวน 4 ตัวที่ทุกตัวดึงเสียงเข้าใจ — pollIntervalMinutes (int อยู่ในขอบเขต schedule ของตัวดึงเสียง), pollTimes (เวลาดึงประจำวัน ดูด้านล่าง), agentFilter (เมื่อ features.agentFilter) และ objectFilter (เมื่อ features.objectFilter) — key อื่นที่ไม่รู้จักจะถูกตัดทิ้ง

กำหนดเวลาดึง — ทุก N นาที หรือตามเวลาที่ตั้งไว้: ปกติระบบจะดึงสายจากการเชื่อมต่อทุก pollIntervalMinutes นาที (ถ้าไม่ส่ง จะใช้ schedule.defaultMinutes ของตัวดึงเสียง) ถ้าต้องการให้ดึงตามเวลาที่กำหนดในแต่ละวันแทน ให้ส่ง pollTimes:

FieldTypeNote
pollTimesstring[]optional — รูปแบบ "HH:MM" แบบ 24 ชั่วโมง (00:00–23:59) เวลาประเทศไทย (Asia/Bangkok) ทุกวัน ใส่ได้ 1-24 เวลา ระบบจะเรียงเวลาให้และตัดเวลาที่ซ้ำออก ระหว่างที่ตั้งไว้ ระบบจะไม่ใช้ pollIntervalMinutes แต่ยังเก็บค่าไว้ กลับไปใช้รอบดึงเมื่อไรก็ได้ค่าเดิม ส่ง [] เพื่อลบเวลาที่ตั้งไว้และกลับไปดึงตามรอบ
{ "config": { "pollTimes": ["08:00", "12:30", "18:00"] } }

สามชนิดในตัวยังใช้โครงสร้างเดิมตามด้านล่าง

config สำหรับ threecx:

FieldTypeNote
baseUrlurlrequired
usernamestringrequired
passwordstringrequired (write-only)
headlessbooleanoptional
pollIntervalMinutesint 5-1440optional
pollTimesstring[]optional — ดูด้านบน

config สำหรับ dtac:

FieldTypeNote
baseUrlurloptional
usernamestringrequired
passwordstringrequired (write-only)
pollIntervalMinutesint 5-1440optional
pollTimesstring[]optional — ดูด้านบน

config สำหรับ minio:

bucket แบบ S3 (MinIO, AWS S3, Wasabi, …) ที่ตู้สาขาของคุณเขียนไฟล์เสียงลงไปอยู่แล้ว — เราอ่านอย่างเดียว ไม่เขียนกลับ

FieldTypeNote
endpointstringrequired — ใส่เป็น host[:port] หรือ URL เต็ม http(s):// ก็ได้ ถ้ามี scheme จะใช้ scheme ตัดสินว่าใช้ TLS หรือไม่
accessKeystringrequired
secretKeystringrequired (write-only)
bucketstringrequired
useSSLbooleanoptional (default false) — ใช้เฉพาะตอน endpoint ไม่มี scheme
prefixstringoptional — โฟลเดอร์ที่จะไล่ดู เช่น recordings/2026/ ถ้าเว้นว่างจะไล่ทั้ง bucket
regionstringoptional
extensionsstring[]optional — นามสกุลไฟล์เสียงที่จะดึง ตัวพิมพ์เล็กและไม่ต้องมีจุด ถ้าไม่ใส่ จะใช้นามสกุลไฟล์เสียงทั้งหมด (wav, mp3, m4a, ogg, opus, flac, …) เพราะ bucket ที่ตู้สาขาเขียนลงมักมี CDR csv/json และไฟล์ .tmp ปนอยู่ด้วย จึงต้องเป็น allowlist ไม่ใช่ blocklist
direction"in" | "out"optional (default "in") — bucket ไม่บอกว่าเป็นสายเข้าหรือสายออก คุณจึงต้องระบุเองว่าโฟลเดอร์นี้เก็บสายแบบไหน แล้วระบบจะใส่ค่านี้ให้ทุกสายที่ดึงเข้ามา
lookbackHoursint 1-720optional (default 48) — ระบบดูเฉพาะไฟล์ที่แก้ไขภายในช่วงนี้ จึงไม่ต้องไล่ bucket ที่เก็บไฟล์หลายปีใหม่ทั้งหมดทุกรอบ
pollIntervalMinutesint 5-1440optional (default 15)
pollTimesstring[]optional — ดูด้านบน
maxObjectsPerTickint 1-5000optional (default 500) — เพดานกันงานบานในรอบเดียว

เงื่อนไขการเก็บสาย — config.agentFilter (ตัวดึงเสียงที่มี features.agentFilter เช่นชนิดโทรศัพท์, optional):

เก็บเฉพาะสายของพนักงานที่เลือก โดยกรองตั้งแต่ ก่อนดาวน์โหลด สายที่กรองออกจะไม่ถูกแปลงไฟล์ (transcode) วิเคราะห์ หรือคิดเงิน ถ้าไม่ใส่ (หรือใช้ mode: "all") จะเก็บทุกสาย ดูค่า keys ที่ใช้ได้จาก GET /providers/{id}/people

FieldTypeNote
mode"all" | "include" | "exclude"default "all"
keysstring[]พนักงานที่จะเก็บ/ยกเว้น — ค่า key จาก /people (3CX: extension · DTAC: user id)
directions("in" | "out")[]optional — ถ้าไม่ใส่ จะเก็บทั้งสายเข้าและสายออก
minDurationSecint 0-3600optional — ตัดสายที่สั้นกว่านี้
maxDurationSecint 0-28800optional — ตัดสายที่ยาวกว่านี้ ถ้าใส่ค่าน้อยกว่า minDurationSec ระบบจะไม่ใช้ค่านี้ (ไม่งั้นจะไม่เหลือสายเลย)

เงื่อนไขการเก็บสาย — config.objectFilter (ตัวดึงเสียงที่มี features.objectFilter เช่น minio, optional):

bucket ไม่มีรายชื่อพนักงาน สิ่งที่ตั้งเงื่อนไขได้จริงจึงมีสองอย่าง: ไฟล์ อยู่ที่ไหน และเสียง ยาวเท่าไร สองส่วนนี้บังคับใช้คนละจุดและมีผลกับค่าใช้จ่ายต่างกัน — folders ถูกคัดตอน LIST ของ S3 คือก่อนโหลดแม้แต่ไบต์เดียว ส่วนความยาวไม่มีอยู่ในผลลิสต์ จึงตรวจได้หลังโหลดไฟล์มา probe เท่านั้น เสียค่าโหลด 1 ครั้ง แต่ยังข้ามการเก็บไฟล์ การสร้างสาย การแปลงไฟล์ (transcode) การถอดเสียง และงาน AI ทุกขั้น

FieldTypeNote
mode"all" | "include" | "exclude"default "all"
foldersstring[]โฟลเดอร์ที่จะเก็บ/ยกเว้น — ใส่เป็น path เต็ม (pbx/2026/08), path ที่เทียบจาก prefix (08 เมื่อ prefix คือ pbx/2026/) หรือชื่อโฟลเดอร์อย่างเดียว (sales) ซึ่งจะตรงกับโฟลเดอร์ชื่อนั้นในชั้นไหนก็ได้
minDurationSecint 0-28800optional — ตัดเสียงที่สั้นกว่านี้
maxDurationSecint 0-28800optional — ตัดเสียงที่ยาวกว่านี้ ถ้าใส่ค่าน้อยกว่า minDurationSec ระบบจะไม่ใช้ค่านี้
curl -X POST https://phone.mcloud.co.th/api/v1/providers \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "threecx",
    "name": "Main PBX",
    "config": {
      "baseUrl": "https://pbx.example.com",
      "username": "apiuser",
      "password": "s3cret"
    },
    "isActive": true
  }'
{ "id": "0192f0...", "connector": "threecx" }

การสร้างแหล่งแบบ bucket เขียนแบบนี้:

curl -X POST https://phone.mcloud.co.th/api/v1/providers \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "minio",
    "name": "PBX drop folder",
    "config": {
      "endpoint": "https://s3.example.com:9000",
      "accessKey": "AKIA...",
      "secretKey": "s3cret",
      "bucket": "recordings",
      "prefix": "pbx/2026/",
      "direction": "in",
      "lookbackHours": 48,
      "pollIntervalMinutes": 15
    },
    "isActive": true
  }'

ตัวดึงเสียงอื่นใช้วิธีเดียวกัน — ส่ง slug เป็น kind และใส่ fields จาก catalog ลงใน config:

curl -X POST https://phone.mcloud.co.th/api/v1/providers \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "<slug จาก /providers/catalog>",
    "name": "Branch PBX",
    "config": { "<key ของช่อง>": "<ค่า>", "pollIntervalMinutes": 10 }
  }'

GET /providers/{id}

ดึง provider หนึ่งตัว — จะไม่คืนค่าลับใด ๆ มีแค่ส่วนที่ไม่ลับ

Scopes: manage_provider_config

ErrorStatusเมื่อไหร่
provider_not_found404ไม่มี provider นี้ในธุรกิจนี้
provider_kind_not_supported422kind ที่เก็บไว้ไม่ใช่แหล่งดึงสาย (เช่นเป็นค่าตั้งของโมเดล AI)

config คือค่าตั้งค่าที่ไม่ลับของตัวดึงเสียง — ช่องตั้งค่าของตัวดึงเสียงนั้น รวมกับ pollIntervalMinutes / pollTimes / agentFilter / objectFilter เมื่อตั้งไว้ — มีให้กับทุกตัวดึงเสียง ส่วนสามชนิดในตัวยังมีบล็อกแบบแบนเหมือนเดิม: response มีทั้งสองฝั่งเสมอ ฝั่งที่ไม่เกี่ยวจะเป็น null — client จึงไม่ต้องแยกเงื่อนไขตาม kind เพียงเพื่ออ่านค่า ระบบโทรศัพท์จะเติม baseUrl / username / agentFilter แล้วปล่อย minio เป็น null:

{
  "id": "0192f0...",
  "kind": "threecx",
  "connector": "threecx",
  "name": "Main PBX",
  "isActive": true,
  "config": {
    "baseUrl": "https://pbx.example.com",
    "username": "apiuser",
    "agentFilter": { "mode": "include", "keys": ["12110", "12117"] }
  },
  "baseUrl": "https://pbx.example.com",
  "username": "apiuser",
  "agentFilter": {
    "mode": "include",
    "keys": ["12110", "12117"],
    "directions": ["in", "out"],
    "minDurationSec": 10,
    "maxDurationSec": 3600
  },
  "minio": null
}

ส่วน bucket จะกลับกัน — คืนทุกอย่างยกเว้น secretKey:

{
  "id": "0192f1...",
  "kind": "minio",
  "connector": "minio",
  "name": "PBX drop folder",
  "isActive": true,
  "config": { "endpoint": "https://s3.example.com:9000", "bucket": "recordings", "…": "…" },
  "baseUrl": null,
  "username": null,
  "agentFilter": null,
  "minio": {
    "endpoint": "https://s3.example.com:9000",
    "useSSL": true,
    "accessKey": "AKIA...",
    "bucket": "recordings",
    "prefix": "pbx/2026/",
    "region": null,
    "extensions": ["wav", "mp3", "m4a"],
    "direction": "in",
    "lookbackHours": 48,
    "pollIntervalMinutes": 15,
    "pollTimes": null,
    "maxObjectsPerTick": 500,
    "objectFilter": {
      "mode": "include",
      "folders": ["sales"],
      "minDurationSec": 10
    }
  }
}

ตัวดึงเสียงอื่น (kind: "script") จะมี baseUrl, username และ minio เป็น null และค่าทั้งหมดอยู่ใน config ส่วน agentFilter คือค่าเดียวกับ config.agentFilter

PATCH /providers/{id}

แก้ provider — ระบบจะ merge config เข้ากับค่าที่เก็บไว้ จึงส่งเฉพาะ key ที่ต้องการเปลี่ยนได้ (เช่นเปลี่ยน password โดยไม่ต้องส่ง baseUrl ซ้ำ) ถ้าส่ง secret มาว่าง ระบบจะใช้ค่าเดิม แล้วตรวจผลที่ merge แล้วตามช่องตั้งค่าของตัวดึงเสียง

Scopes: manage_provider_config

ParamTypeNote
namestring (2-60)optional
configobjectoptional — merge เข้ากับ config ที่เก็บไว้
isActivebooleanoptional
ErrorStatusเมื่อไหร่
connector_not_available403แก้ config หรือส่ง isActive: true ขณะที่ตัวดึงเสียงถูกปิดหรือไม่ได้เปิดให้ธุรกิจของคุณแล้ว — การเปลี่ยนชื่อและการปิดยังทำได้ · การปิดตัวดึงเสียงอาจไม่กระทบการเชื่อมต่อ 3CX, DTAC และ MinIO ที่มีอยู่ กรณีนั้นการแก้ไขจะสำเร็จตามปกติ
provider_not_found404ไม่มี provider นี้ในธุรกิจนี้
provider_name_taken409มี provider อื่นใช้ name นี้แล้ว
validation_failed422config หลัง merge ไม่ตรงกับช่องตั้งค่าของตัวดึงเสียง
curl -X PATCH https://phone.mcloud.co.th/api/v1/providers/$ID \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{"config":{"password":"r0tated"}}'

ตั้งเงื่อนไขการเก็บสายด้วยวิธีเดียวกัน — ส่ง config.agentFilter (merge เข้าไป จึงไม่กระทบ credential):

curl -X PATCH https://phone.mcloud.co.th/api/v1/providers/$ID \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{"config":{"agentFilter":{"mode":"include","keys":["12110","12117"]}}}'

ถ้าเป็น bucket ให้ใช้ config.objectFilter แทน — merge แบบเดียวกัน และไม่แตะ credential เหมือนกัน:

curl -X PATCH https://phone.mcloud.co.th/api/v1/providers/$ID \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{"config":{"objectFilter":{"mode":"include","folders":["sales"],"minDurationSec":10}}}'
{ "ok": true }

เปลี่ยนไปดึงตามเวลาที่ตั้งไว้ด้วย config.pollTimes และส่งรายการว่างเพื่อกลับไปดึงตามรอบ:

curl -X PATCH https://phone.mcloud.co.th/api/v1/providers/$ID \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{"config":{"pollTimes":["08:00","12:30","18:00"]}}'

curl -X PATCH https://phone.mcloud.co.th/api/v1/providers/$ID \
  -H "Authorization: Bearer crk_..." \
  -H "Content-Type: application/json" \
  -d '{"config":{"pollTimes":[]}}'

การ merge เป็นราย key ไม่ merge ลึกลงไปข้างใน: การส่ง objectFilter (หรือ agentFilter หรือ pollTimes) คือการแทนที่ค่าทั้งก้อน ไม่ใช่แก้ทีละส่วน ให้ส่งเงื่อนไขที่ต้องการมาให้ครบ และถ้าจะล้างเงื่อนไขให้ส่ง {"mode":"all"}

DELETE /providers/{id}

ลบ config ของ provider — เรียกซ้ำได้ (idempotent) id ที่ถูกลบไปแล้วก็ยังตอบ ok

Scopes: manage_provider_config

ErrorStatusเมื่อไหร่
provider_kind_not_supported422มี id นี้อยู่จริง แต่ไม่ใช่แหล่งดึงสาย (เป็นค่าตั้งของโมเดล AI)
{ "ok": true }

GET /providers/{id}/people

ดึงรายชื่อพนักงานที่เลือกได้จาก provider นี้ เพื่อหา key ที่ใช้ตั้งเงื่อนไขการเก็บสาย (config.agentFilter.keys) เป็นการ เรียกสดไปที่ระบบปลายทาง (directory extension ของ 3CX / รายชื่อ user ของ DTAC / ตัวดึงเสียงใดก็ได้ที่ catalog บอก features.people) จึงอาจใช้เวลาสองสามวินาที

ใช้ได้เฉพาะตัวดึงเสียงที่มีรายชื่อ — minio เป็นที่เก็บไฟล์ ไม่ใช่ตู้สาขา ไม่มีบัญชีให้ล็อกอินและไม่มีรายชื่อให้อ่าน อีกทั้งเงื่อนไขของถังเลือกที่ "โฟลเดอร์" ไม่ใช่ "คน" จึงตอบ provider_kind_not_supported (422) โดยเจตนา ให้ไปดูรายการไฟล์ในถังจากหน้า ธุรกิจ → ตั้งค่าธุรกิจ → ผู้ให้บริการ ในหน้าเว็บ แล้วนำ path ที่เห็นมาใส่ใน config.objectFilter.folders

Scopes: manage_provider_config

Fieldความหมาย
providerslug ของตัวดึงเสียง
keyค่าที่ใส่ใน agentFilter.keys — extension (3CX) / user id (DTAC)
nameชื่อที่แสดง
numberค่าที่จะปรากฏเป็น callcenterNumber ของสาย — 3CX: extension · DTAC: เบอร์โทร
detailค่าเดียวกับ number (คงไว้ให้ระบบที่เชื่อมไว้แล้ว)
activeพนักงานคนนี้ยังเปิดใช้งานอยู่ที่ provider หรือไม่
ErrorStatusเมื่อไหร่
connector_not_available403ตัวดึงเสียงถูกปิด หรือไม่ได้เปิดให้ธุรกิจของคุณแล้ว · การปิดตัวดึงเสียงอาจไม่กระทบการเชื่อมต่อ 3CX, DTAC และ MinIO ที่มีอยู่ กรณีนั้นคำขอนี้จะสำเร็จตามปกติ
provider_not_found404ไม่มี provider นี้ในธุรกิจนี้
provider_kind_not_supported422ตัวดึงเสียงนี้ไม่มีรายชื่อ
provider_people_unavailable502ล็อกอินหรือดึงรายชื่อจาก provider ไม่สำเร็จ (ข้อมูลล็อกอินผิด หรือ provider ล่ม)
curl https://phone.mcloud.co.th/api/v1/providers/$ID/people \
  -H "Authorization: Bearer crk_..."
{
  "provider": "threecx",
  "people": [
    { "key": "12110", "name": "Jintana Promdee", "number": "12110", "detail": "12110", "active": true }
  ]
}

POST /providers/{id}/test

ทดสอบการเชื่อมต่อด้วย credential ที่เก็บไว้ ตัวดึงเสียงจะตรวจด้วยวิธีของตัวเองในเครื่องรันที่แยกไว้ — สำหรับ minio คือทั้งข้อมูลล็อกอิน และสิทธิ์มองเห็นถัง สำหรับระบบโทรศัพท์คือการล็อกอินจริง การทดสอบที่ได้รันแล้วจะคืน 200 ทั้งสองกรณี ถ้าเชื่อมต่อไม่สำเร็จ ระบบจะแจ้งใน body ไม่ใช่เป็น HTTP error

Scopes: manage_provider_config

Field ใน bodyความหมาย
oktrue = เชื่อมต่อได้
errorเมื่อ ok เป็น false: auth_failed · unreachable · rate_limited · bad_config · provider_error · failed (กรณีอื่นทั้งหมด — รายละเอียดอยู่ในบันทึกการทำงานของแพลตฟอร์ม)
messageประโยคจากตัวดึงเสียงเอง — มีค่าเมื่อสำเร็จ และเมื่อเป็น bad_config นอกนั้นเป็น null
ErrorStatusเมื่อไหร่
connector_not_available403ตัวดึงเสียงถูกปิด หรือไม่ได้เปิดให้ธุรกิจของคุณแล้ว · การปิดตัวดึงเสียงอาจไม่กระทบการเชื่อมต่อ 3CX, DTAC และ MinIO ที่มีอยู่ กรณีนั้นคำขอนี้จะสำเร็จตามปกติ
provider_not_found404ไม่มี provider นี้ในธุรกิจนี้
service_unavailable503ตอนนี้ยังเริ่มการทดสอบไม่ได้ — ลองใหม่ภายหลัง
curl -X POST https://phone.mcloud.co.th/api/v1/providers/$ID/test \
  -H "Authorization: Bearer crk_..."
{ "ok": true, "message": "Connected · 14 users" }
{ "ok": false, "error": "auth_failed", "message": null }