ภาพรวม

API นี้สำหรับนักพัฒนาของสำนักงานบัญชีที่ร่วมทดสอบ และซอฟต์แวร์บัญชีที่เชื่อมต่อให้สำนักงาน ทุกอย่างผูกกับสำนักงานของ API key ลูกค้าคือธุรกิจที่สำนักงานทำบัญชีให้ และเอกสารแต่ละใบอยู่ใต้ลูกค้าหนึ่งราย

  • Base URL: https://heybuzzai.com/api/v1
  • รับและตอบเป็น JSON (UTF-8) ยกเว้นการส่งไฟล์ที่ใช้ multipart/form-data
  • เวลาเป็น ISO 8601 (UTC) วันที่บนเอกสารเป็น YYYY-MM-DD ปี ค.ศ. จำนวนเงินเป็นตัวเลขหน่วยบาท
  • ประเภทเอกสาร: accounting-document เอกสารบัญชี (ใบกำกับภาษี ใบกำกับภาษีอย่างย่อ ใบเสร็จ ใบแจ้งหนี้ ใบลดหนี้ ใบเพิ่มหนี้ 50 ทวิ) และ bank-statement สเตทเมนต์ธนาคาร
  • ข้อผิดพลาดทุกแบบตอบเป็น {"error": {"code", "message", "message_en"}} ให้โค้ดตัดสินใจจาก code ซึ่งไม่เปลี่ยน ส่วนข้อความเป็นภาษาไทยและอังกฤษสำหรับแสดงคน

ขอ API key

ตอนนี้ทีม buzzAI เป็นผู้ออก API key ให้ อีเมลมาที่ [email protected] พร้อมชื่อสำนักงานและชื่อโปรแกรมที่จะเชื่อมต่อ สำนักงานหนึ่งมีได้หลาย key เช่น แยกตามโปรแกรม

  • key มีรูปแบบ bz_live_… เราแสดงให้เห็นครั้งเดียวตอนออก และเก็บเพียงค่าแฮช จึงดูย้อนหลังไม่ได้ ถ้าทำหายให้ขอใหม่
  • เก็บ key ไว้ฝั่งเซิร์ฟเวอร์ (เช่น ตัวแปร environment) ห้ามใส่ในหน้าเว็บ แอปมือถือ หรือ Git API นี้ไม่เปิดให้เรียกจากเบราว์เซอร์โดยตรง
  • ถ้าสงสัยว่า key รั่ว แจ้งทีมให้ยกเลิกได้ทันที key ที่ยกเลิกแล้วใช้ไม่ได้อีก
  • ทีมกำหนดโควตารายเดือนต่อ key ได้ (ดู การคิดเงินและโควตา)

การยืนยันตัวตน

ส่ง key ในหัวข้อ Authorization ทุกคำขอ

Authorization: Bearer bz_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

ไม่มีหัวข้อนี้ได้ 401 missing_api_key key ผิดหรือถูกยกเลิกได้ 401 invalid_api_key เอกสารหรือลูกค้าของสำนักงานอื่นตอบ 404 เสมอ เหมือนไม่มีอยู่

เริ่มต้นใน 2 นาที

ส่งใบกำกับภาษีหนึ่งไฟล์และรอผล (wait=true รอได้สูงสุด 60 วินาที)

curl https://heybuzzai.com/api/v1/documents \
  -H "Authorization: Bearer $BUZZAI_API_KEY" \
  -H "Idempotency-Key: 6f1c2d0e-invoice-00123" \
  -F "[email protected]" \
  -F "type=accounting-document" \
  -F "wait=true"

ได้ 201 เมื่ออ่านเสร็จ ข้อมูลอยู่ใน data[0].data

{
  "data": [
    {
      "id": "b1c3e1d2-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
      "object": "document",
      "type": "accounting-document",
      "status": "ready",
      "client_id": "7f0e6d5c-4b3a-4291-8f7e-6d5c4b3a2918",
      "file": {
        "name": "IV2026-00123.pdf",
        "mime_type": "application/pdf",
        "size": 184220
      },
      "part": null,
      "units": 1,
      "pages": null,
      "checks": {
        "passed": true,
        "issues": []
      },
      "corrected": false,
      "error": null,
      "data": {
        "document_type": "tax_invoice",
        "document_number": "IV2026-00123",
        "document_date": "2026-10-05",
        "reference_document_number": null,
        "currency": "THB",
        "issuer": {
          "name": "บริษัท ตัวอย่าง จำกัด",
          "tax_id": "9905556123450",
          "branch": "00000",
          "address": "99 ถนนสุขุมวิท กรุงเทพฯ 10110"
        },
        "recipient": {
          "name": "บริษัท ลูกค้า จำกัด",
          "tax_id": "9915556123450",
          "branch": "00000",
          "address": null
        },
        "vat_treatment": "separated",
        "amounts": {
          "subtotal": 1000,
          "discount": null,
          "service_charge": null,
          "vat": 70,
          "total": 1070,
          "withholding_tax": null
        },
        "amount_in_words": "หนึ่งพันเจ็ดสิบบาทถ้วน",
        "withholding": {
          "form": null,
          "lines": []
        },
        "items": [
          {
            "description": "ค่าบริการทำบัญชี เดือนกันยายน",
            "quantity": 1,
            "unit_price": 1000,
            "amount": 1000
          }
        ]
      },
      "created_at": "2026-10-21T03:00:00.000Z",
      "updated_at": "2026-10-21T03:00:12.000Z",
      "confirmed_at": null
    }
  ]
}

ถ้าอ่านไม่ทันเวลารอ จะได้ 202 พร้อมสถานะ processing ให้ถามผลด้วย GET /documents/{id} ทุก 2–5 วินาที

Endpoints

POST /documents

ส่งไฟล์ 1 ไฟล์ต่อคำขอ แบบ multipart/form-data

ช่องจำเป็นคำอธิบาย
fileใช่ไฟล์เดียว เอกสารบัญชี: JPEG, PNG, WebP ไม่เกิน 7 MB หรือ PDF ไม่เกิน 15 MB · สเตทเมนต์: JPEG, PNG ไม่เกิน 7 MB หรือ PDF ไม่เกิน 15 MB และไม่เกิน 8 หน้า ระบบดูชนิดไฟล์จากเนื้อไฟล์ ไม่ใช่นามสกุล
typeไม่accounting-document (ค่าเริ่มต้น) หรือ bank-statement
client_idไม่ลูกค้าของสำนักงาน (จาก GET /clients) ถ้าไม่ระบุ เอกสารจะอยู่ใต้ลูกค้า “ไม่ระบุลูกค้า (ส่งผ่าน API)” ที่ระบบสร้างให้สำนักงานอัตโนมัติ แนะนำให้ระบุเพื่อให้ตรวจเลขผู้เสียภาษีของลูกค้าในเอกสารได้
waitไม่true รอผลสูงสุด 60 วินาที, จำนวนวินาที 1–60 หรือ false (ค่าเริ่มต้น ตอบทันที) ส่งเป็นช่องในฟอร์มหรือ ?wait= ก็ได้

หัวข้อ Idempotency-Key (ไม่บังคับ แต่แนะนำ) ดู ส่งซ้ำอย่างปลอดภัย

คำตอบ: {"data": [Document, …]} ไฟล์เดียวที่มีหลายใบ (เช่น สแกนใบเสร็จหลายใบใน PDF เดียว) ระบบแยกเป็นหลายเอกสาร ใช้ wait จะได้ครบทุกใบเรียงตามลำดับในไฟล์ ถ้าไม่รอ ใบที่เหลือจะตามมาเป็นเอกสารใหม่ที่ part.source_document_id ชี้ไปที่เอกสารแรก และ GET /documents/{id} ของเอกสารแรกบอกรหัสทุกใบใน part.document_ids

สถานะ HTTPความหมาย
201อ่านเสร็จทุกเอกสารจากไฟล์นี้แล้ว (ใช้ wait)
202รับไฟล์แล้ว ยังอ่านไม่เสร็จ
400 402 404 409 413 415 422 429ดู รหัสข้อผิดพลาด คำขอที่ถูกปฏิเสธไม่สร้างเอกสารและไม่คิดเงิน

GET /documents/{id}

สถานะ ผลการตรวจ และข้อมูลของเอกสารหนึ่งใบ (ดู ออบเจกต์เอกสาร)

{
  "id": "c2d4f2e3-5b6c-4d7e-9fa0-1b2c3d4e5f60",
  "object": "document",
  "type": "accounting-document",
  "status": "in_review",
  "client_id": "7f0e6d5c-4b3a-4291-8f7e-6d5c4b3a2918",
  "file": {
    "name": "receipt.jpg",
    "mime_type": "image/jpeg",
    "size": 912004
  },
  "part": null,
  "units": 1,
  "pages": null,
  "checks": {
    "passed": false,
    "issues": [
      {
        "code": "amount_mismatch",
        "message": "ยอดเงินบวกลบไม่ลงตัว",
        "message_en": "The amounts do not add up."
      }
    ]
  },
  "corrected": false,
  "error": null,
  "data": null,
  "created_at": "2026-10-21T03:00:00.000Z",
  "updated_at": "2026-10-21T03:00:12.000Z",
  "confirmed_at": null
}

GET /documents

เอกสารของสำนักงาน ใหม่สุดก่อน แต่ละรายการเป็นออบเจกต์เอกสารเต็ม (มี data เมื่อพร้อม)

พารามิเตอร์คำอธิบาย
statusคั่นด้วยจุลภาค เช่น ready,confirmed
typeaccounting-document หรือ bank-statement
client_idเฉพาะลูกค้ารายนี้
sinceเอกสารที่เปลี่ยนแปลง (ส่งเข้ามา อ่านเสร็จ หรือยืนยัน) ตั้งแต่เวลานี้: 2026-10-01 (เที่ยงคืนเวลาไทย) หรือ 2026-10-01T09:00:00+07:00 เหมาะกับการดึงข้อมูลเป็นรอบ
limit1–100 (ค่าเริ่มต้น 50)
cursorค่า next_cursor จากหน้าก่อน ถ้าเป็น null คือหน้าสุดท้าย
{
  "data": [
    {
      "id": "d3e5a3f4-6c7d-4e8f-a0b1-2c3d4e5f6071",
      "object": "document",
      "type": "bank-statement",
      "status": "ready",
      "client_id": "7f0e6d5c-4b3a-4291-8f7e-6d5c4b3a2918",
      "file": {
        "name": "statement-2026-09.pdf",
        "mime_type": "application/pdf",
        "size": 402311
      },
      "part": null,
      "units": 3,
      "pages": 3,
      "checks": {
        "passed": true,
        "issues": [],
        "results": [
          {
            "code": "balance_mismatch",
            "description": "ยอดยกมา + ฝาก − ถอน = ยอดยกไป",
            "description_en": "Opening balance + deposits − withdrawals = closing balance",
            "passed": true,
            "rows": []
          },
          {
            "code": "running_balance_mismatch",
            "description": "ยอดคงเหลือแต่ละรายการต่อจากรายการก่อน",
            "description_en": "Each running balance follows from the row before",
            "passed": true,
            "rows": []
          }
        ]
      },
      "corrected": false,
      "error": null,
      "data": {
        "account": {
          "bank": "ธนาคารตัวอย่าง",
          "name": "บริษัท ลูกค้า จำกัด",
          "number": "xxx-x-x5678-x"
        },
        "currency": "THB",
        "period": {
          "start": "2026-09-01",
          "end": "2026-09-30"
        },
        "balances": {
          "opening": 10000,
          "closing": 12300.5
        },
        "totals": {
          "deposits": 5000.5,
          "withdrawals": 2700
        },
        "transactions": [
          {
            "date": "2026-09-02",
            "description": "รับโอนเงิน",
            "withdrawal": null,
            "deposit": 5000.5,
            "balance": 15000.5
          },
          {
            "date": "2026-09-15",
            "description": "ค่าเช่าสำนักงาน",
            "withdrawal": 2500,
            "deposit": null,
            "balance": 12500.5
          },
          {
            "date": "2026-09-30",
            "description": "ค่าธรรมเนียม",
            "withdrawal": 200,
            "deposit": null,
            "balance": 12300.5
          }
        ]
      },
      "created_at": "2026-10-21T03:00:00.000Z",
      "updated_at": "2026-10-21T03:00:12.000Z",
      "confirmed_at": null
    }
  ],
  "next_cursor": "MjAyNi0xMC0yMVQwMzowMDowMC4wMDBafGQzZTVh"
}

GET /clients

ลูกค้าทั้งหมดของสำนักงาน: {"data": [{"id", "object": "client", "name", "tax_id"}]}

POST /clients

เพิ่มลูกค้า ส่ง JSON {"name": "…", "tax_id": "…"} ชื่อ 1–200 ตัวอักษร เลขผู้เสียภาษี 13 หลัก (ไม่บังคับ ขีดและช่องว่างถูกตัดออก) ได้ 201 พร้อมลูกค้าที่สร้าง

{
  "id": "7f0e6d5c-4b3a-4291-8f7e-6d5c4b3a2918",
  "object": "client",
  "name": "บริษัท ลูกค้า จำกัด",
  "tax_id": "9915556123450"
}

GET /usage

การใช้งานเดือนนี้ (ตามเวลาไทย): หน่วยของสำนักงานรวมทุกช่องทาง สิทธิ์ทดลองฟรีที่เหลือและวันหมด แพ็กเกจและโควตาที่เหลือ และโควตาของ key ที่ใช้เรียก

{
  "object": "usage",
  "month": "2026-10",
  "period": {
    "start": "2026-09-30T17:00:00.000Z",
    "end": "2026-10-31T17:00:00.000Z"
  },
  "units": 1250,
  "free_trial": {
    "total": 200,
    "used": 200,
    "remaining": 0,
    "started_at": "2026-09-12T02:15:00.000Z",
    "ends_at": "2026-10-12T02:15:00.000Z"
  },
  "plan": {
    "id": "pro",
    "name": "Pro",
    "monthly_units": 5000,
    "used": 1180,
    "remaining": 3820
  },
  "key": {
    "id": "0c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
    "name": "ระบบบัญชีของสำนักงาน",
    "prefix": "bz_live_Xy12ab",
    "monthly_cap": 3000,
    "used": 940,
    "remaining": 2060
  }
}

plan เป็น null เมื่อสำนักงานจ่ายตามจำนวนเอกสาร plan.used นับเฉพาะหน่วยที่เกินสิทธิ์ทดลองฟรี key.monthly_cap เป็น null เมื่อ key ไม่มีโควตา

ออบเจกต์เอกสาร

ช่องคำอธิบาย
idรหัสเอกสาร
typeaccounting-document หรือ bank-statement
statusดู สถานะเอกสาร
client_idลูกค้าที่เอกสารนี้อยู่ใต้
fileชื่อไฟล์ ชนิด (mime_type) และขนาดเป็นไบต์
partเมื่อไฟล์เดียวมีหลายใบ: index ใบที่เท่าไร of จากทั้งหมด และ source_document_id เอกสารแรกของไฟล์ ไม่ใช่ไฟล์หลายใบเป็น null
unitsหน่วยที่คิดเงิน: null ระหว่างอ่าน, 0 เมื่อ failed หรือ rejected
pagesจำนวนหน้าของสเตทเมนต์ (เอกสารบัญชีเป็น null)
checkspassed ผ่านทุกข้อหรือไม่ และ issues เหตุที่ต้องตรวจ (รหัสด้านล่าง) สเตทเมนต์มี results ผลการตรวจแต่ละข้อพร้อมลำดับรายการที่ไม่ผ่าน มีเมื่ออ่านเสร็จ
correctedคนตรวจทานแก้ข้อมูลก่อนยืนยัน
errorเมื่อ failed: code และข้อความ
dataข้อมูลที่อ่านได้ มีเฉพาะสถานะ ready และ confirmed
created_at updated_at confirmed_atเวลาส่งเข้ามา เปลี่ยนแปลงล่าสุด และยืนยัน

รูปแบบข้อมูล

เอกสารบัญชี (accounting-document)

ช่องที่อ่านไม่ได้หรือไม่มีในเอกสารเป็น null เลขผู้เสียภาษีและสาขาเป็นข้อความ (เก็บเลข 0 นำหน้า) สาขา 00000 คือสำนักงานใหญ่

{
  "document_type": "tax_invoice",
  "document_number": "IV2026-00123",
  "document_date": "2026-10-05",
  "reference_document_number": null,
  "currency": "THB",
  "issuer": {
    "name": "บริษัท ตัวอย่าง จำกัด",
    "tax_id": "9905556123450",
    "branch": "00000",
    "address": "99 ถนนสุขุมวิท กรุงเทพฯ 10110"
  },
  "recipient": {
    "name": "บริษัท ลูกค้า จำกัด",
    "tax_id": "9915556123450",
    "branch": "00000",
    "address": null
  },
  "vat_treatment": "separated",
  "amounts": {
    "subtotal": 1000,
    "discount": null,
    "service_charge": null,
    "vat": 70,
    "total": 1070,
    "withholding_tax": null
  },
  "amount_in_words": "หนึ่งพันเจ็ดสิบบาทถ้วน",
  "withholding": {
    "form": null,
    "lines": []
  },
  "items": [
    {
      "description": "ค่าบริการทำบัญชี เดือนกันยายน",
      "quantity": 1,
      "unit_price": 1000,
      "amount": 1000
    }
  ]
}
document_typeประเภท
tax_invoiceใบกำกับภาษี
abbreviated_tax_invoiceใบกำกับภาษีอย่างย่อ
invoiceใบแจ้งหนี้
receiptใบเสร็จรับเงิน
withholding_tax_certificate50 ทวิ
credit_noteใบลดหนี้
debit_noteใบเพิ่มหนี้
otherอื่น ๆ
vat_treatmentความหมาย
separatedแยก VAT
noneไม่มี VAT
included_not_shownรวม VAT ไม่แยกยอด

withholding คือภาษีหัก ณ ที่จ่าย: form แบบ ภ.ง.ด. และ lines รายการเงินได้ในหนังสือรับรอง 50 ทวิ (income_type date_paid amount tax) ส่วน items คือรายการสินค้าหรือบริการ

สเตทเมนต์ธนาคาร (bank-statement)

รายการเรียงจากเก่าไปใหม่ แต่ละรายการมีถอน (withdrawal) หรือฝาก (deposit) อย่างใดอย่างหนึ่ง เลขบัญชีตามที่พิมพ์ (อาจถูกปิดบางหลัก) totals มีเฉพาะเมื่อพิมพ์ไว้บนสเตทเมนต์ รองรับเฉพาะบัญชีเงินบาท ไม่เกิน 8 หน้าและ 200 รายการต่อไฟล์

{
  "account": {
    "bank": "ธนาคารตัวอย่าง",
    "name": "บริษัท ลูกค้า จำกัด",
    "number": "xxx-x-x5678-x"
  },
  "currency": "THB",
  "period": {
    "start": "2026-09-01",
    "end": "2026-09-30"
  },
  "balances": {
    "opening": 10000,
    "closing": 12300.5
  },
  "totals": {
    "deposits": 5000.5,
    "withdrawals": 2700
  },
  "transactions": [
    {
      "date": "2026-09-02",
      "description": "รับโอนเงิน",
      "withdrawal": null,
      "deposit": 5000.5,
      "balance": 15000.5
    },
    {
      "date": "2026-09-15",
      "description": "ค่าเช่าสำนักงาน",
      "withdrawal": 2500,
      "deposit": null,
      "balance": 12500.5
    },
    {
      "date": "2026-09-30",
      "description": "ค่าธรรมเนียม",
      "withdrawal": 200,
      "deposit": null,
      "balance": 12300.5
    }
  ]
}

เหตุที่ต้องตรวจ (checks.issues[].code)

codeความหมาย
total_illegibleยอดรวมอ่านไม่ออก
tax_id_illegibleเลขผู้เสียภาษีอ่านไม่ออก
date_illegibleวันที่อ่านไม่ออก
doc_number_illegibleเลขที่เอกสารอ่านไม่ออก
document_illegibleภาพไม่ชัด อ่านเอกสารไม่ได้
multiple_documentsมีเอกสารมากกว่า 1 ใบในไฟล์
date_ambiguousวันที่ตีความได้หลายแบบ
page_incompleteเอกสารไม่ครบ หรือบางส่วนอยู่นอกภาพ
altered_documentมีการขีดฆ่าหรือแก้ตัวเลข
unsupported_documentไม่ใช่เอกสารประเภทที่รับ
amount_mismatchยอดเงินบวกลบไม่ลงตัว
vat_rate_mismatchVAT ไม่ตรงอัตราที่ใช้
amount_words_mismatchยอดตัวอักษรไม่ตรงกับตัวเลข
invalid_tax_idเลขผู้เสียภาษีไม่ถูกต้อง (หลักตรวจสอบผิด)
wht_rate_mismatchหัก ณ ที่จ่ายไม่ตรงอัตราที่ใช้ได้
items_mismatchรายการรวมไม่ตรงยอด
invalid_branchรหัสสาขาไม่ใช่ตัวเลข 5 หลัก
date_out_of_rangeวันที่อยู่ในอนาคต หรือเก่าเกิน 5 ปี
client_not_partyไม่พบเลขผู้เสียภาษีของลูกค้าในเอกสาร
vat_missingไม่มียอด VAT แยก หรืออ่านยอด VAT ไม่ได้
amount_unconfirmedยืนยันยอดเงินไม่ได้ ควรตรวจกับเอกสารต้นฉบับ
multiple_accountsมีมากกว่า 1 บัญชีในไฟล์
balance_mismatchยอดยกมา + ฝาก − ถอน ไม่เท่ายอดยกไป
running_balance_mismatchยอดคงเหลือบางรายการไม่ต่อจากรายการก่อน
totals_mismatchยอดรวมฝากหรือถอนที่พิมพ์ไม่ตรงกับผลรวมรายการ
amount_sideบางรายการมีทั้งยอดฝากและถอน หรือไม่มีทั้งสองอย่าง
date_unreadableอ่านวันที่บางรายการไม่ได้
dates_not_ascendingวันที่รายการไม่เรียงจากเก่าไปใหม่
date_outside_periodมีรายการที่วันที่อยู่นอกช่วงของสเตทเมนต์
nothing_to_checkไม่มียอดยกมา ยอดคงเหลือ หรือยอดรวมให้ตรวจยอดเงิน
needs_attentionควรตรวจเอกสารนี้กับต้นฉบับ

เอกสารที่ยืนยันแล้วยังแสดงเหตุที่ระบบแจ้งไว้ตอนอ่าน เพื่อให้รู้ว่าคนตรวจทานดูเรื่องอะไร

สถานะเอกสาร

statusความหมาย
processingรับไฟล์แล้ว กำลังอ่านและตรวจ ยังไม่มีข้อมูล
in_reviewระบบพบข้อสงสัยอย่างน้อยหนึ่งข้อ (ดู checks.issues) รอคนตรวจทาน ยังไม่ส่งข้อมูลจนกว่าจะยืนยัน
readyอ่านและตรวจผ่านทุกข้อ ใช้ข้อมูลใน data ได้ทันที
confirmedคนตรวจทานยืนยันแล้ว data เป็นค่าที่ยืนยัน (ค่าที่แก้ถ้ามี corrected = true)
failedอ่านไม่สำเร็จ ดูเหตุใน error ไม่คิดเงิน
rejectedถูกปฏิเสธ เช่น ไม่ใช่เอกสารที่รับหรือส่งซ้ำ ไม่คิดเงิน

ลำดับปกติ: processing → ready หรือ in_review → confirmed ใช้ข้อมูลได้เมื่อ ready หรือ confirmed เท่านั้น

เหตุที่อ่านไม่สำเร็จ (error.code)

codeความหมาย
pdf_encryptedไฟล์ PDF มีรหัสผ่าน ปลดรหัสผ่านแล้วส่งใหม่
document_too_longสเตทเมนต์ยาวเกิน 8 หน้า หรือเกิน 200 รายการ แยกเป็นไฟล์ที่สั้นลงแล้วส่งใหม่
currency_not_supportedตอนนี้รองรับเฉพาะสเตทเมนต์บัญชีเงินบาท
processing_failedอ่านเอกสารนี้ไม่สำเร็จ ส่งใหม่อีกครั้ง หรือติดต่อทีม buzzAI

รหัสข้อผิดพลาด

{
  "error": {
    "code": "rate_limited",
    "message": "ส่งคำขอถี่เกินไป รอตามจำนวนวินาทีใน Retry-After แล้วลองใหม่",
    "message_en": "Too many requests. Wait the number of seconds in Retry-After, then retry."
  }
}
codeHTTPความหมาย
missing_api_key401ไม่พบ API key ใส่ในหัวข้อ Authorization: Bearer <API key>
invalid_api_key401API key ไม่ถูกต้อง หรือถูกยกเลิกแล้ว
quota_exceeded402API key นี้ใช้ครบโควตาของเดือนนี้แล้ว ติดต่อทีม buzzAI เพื่อเพิ่มโควตา
not_found404ไม่พบ endpoint นี้
document_not_found404ไม่พบเอกสารนี้
client_not_found404ไม่พบลูกค้านี้ในสำนักงานของคุณ
method_not_allowed405endpoint นี้ไม่รับ method นี้
invalid_request400คำขอไม่ถูกต้อง
invalid_parameter400พารามิเตอร์ไม่ถูกต้อง
invalid_type400type ต้องเป็น accounting-document หรือ bank-statement
file_required400ยังไม่ได้แนบไฟล์ในช่อง file
too_many_files400ส่งได้ครั้งละ 1 ไฟล์ในช่อง file
file_empty400ไฟล์ว่าง
invalid_idempotency_key400Idempotency-Key ต้องเป็นตัวอักษรอังกฤษ ตัวเลข หรือเครื่องหมาย 1–255 ตัว
idempotency_conflict409Idempotency-Key นี้ใช้กับไฟล์หรือข้อมูลอื่นไปแล้ว ใช้ key ใหม่สำหรับคำขอใหม่
idempotency_in_progress409คำขอแรกที่ใช้ Idempotency-Key นี้ยังทำไม่เสร็จ รอสักครู่แล้วลองใหม่
file_too_large413ไฟล์ใหญ่เกินกำหนด
multipart_required415ต้องส่งเป็น multipart/form-data
unsupported_media_type415ต้องส่งเป็น application/json
unsupported_file_type415ไม่รับไฟล์ชนิดนี้
pdf_encrypted422ไฟล์ PDF มีรหัสผ่าน ปลดรหัสผ่านก่อนแล้วส่งใหม่
too_many_pages422สเตทเมนต์ยาวเกินกำหนด แยกเป็นไฟล์ที่สั้นลงแล้วส่งใหม่
rate_limited429ส่งคำขอถี่เกินไป รอตามจำนวนวินาทีใน Retry-After แล้วลองใหม่
internal_error500ระบบขัดข้อง ลองใหม่อีกครั้ง
service_unavailable503ระบบยังไม่พร้อมให้บริการ ลองใหม่ภายหลัง

ลองใหม่ได้อย่างปลอดภัยเมื่อได้ 429, 409 idempotency_in_progress, 500 หรือ 503 (รอตาม Retry-After ถ้ามี และใช้ Idempotency-Key เดิม) ข้อผิดพลาดอื่นต้องแก้คำขอก่อน

Rate limit

แต่ละ key ส่งได้ 60 คำขอต่อนาที (นับเป็นช่วงนาทีตามนาฬิกา) ทุกคำตอบมีหัวข้อ

  • X-RateLimit-Limit: จำนวนคำขอต่อนาที
  • X-RateLimit-Remaining: ที่เหลือในนาทีนี้
  • X-RateLimit-Reset: วินาทีจนเริ่มนาทีถัดไป

เกินแล้วได้ 429 rate_limited พร้อม Retry-After เป็นวินาที รอแล้วส่งใหม่ คำขอที่ถูกจำกัดไม่สร้างเอกสาร ถ้าต้องส่งเอกสารจำนวนมากเป็นประจำ ติดต่อทีมเพื่อคุยเรื่องเพดาน

การคิดเงินและโควตา

  • เอกสารบัญชี: 1 หน่วยต่อเอกสาร ไฟล์เดียวที่มีหลายใบนับตามจำนวนใบ
  • สเตทเมนต์ธนาคาร: 1 หน่วยต่อหน้า รูปภาพนับ 1 หน้า ตัวอย่าง สเตทเมนต์ PDF 3 หน้า = 3 หน่วย (units และ pages ในเอกสาร)
  • นับเฉพาะเอกสารที่อ่านได้ (in_review ready confirmed) เอกสารที่ failed หรือ rejected และคำขอที่ถูกปฏิเสธไม่คิดเงิน
  • ราคาเดียวกับการส่งผ่านทีมงาน: 0.60 บาทต่อหน่วย หรือแพ็กเกจรายเดือน Starter 1,000 หน่วย, Pro 5,000 หน่วย, Business 15,000 หน่วย ส่วนเกินหน่วยละ 0.50 บาท (ดู ราคา)
  • ทดลองฟรี 200 หน่วยต่อสำนักงาน ภายใน 1 เดือนนับจากวันที่ส่งเอกสารใบแรก รวมทุกช่องทาง ดูที่เหลือได้ที่ GET /usage

โควตาต่อ key

ทีมกำหนดโควตารายเดือนเป็นหน่วยให้แต่ละ key ได้ เช่น เพื่อไม่ให้โปรแกรมที่ทำงานผิดพลาดส่งเอกสารเกินงบ ระบบตรวจก่อนรับไฟล์ใหม่ทุกครั้ง: ถ้าหน่วยที่ใช้แล้วในเดือนนี้ รวมเอกสารที่ยังอ่านอยู่และไฟล์ที่กำลังส่ง (สเตทเมนต์ PDF นับตามจำนวนหน้า) จะเกินโควตา คำขอได้ 402 quota_exceeded โดยไม่สร้างเอกสาร โควตาเริ่มใหม่ทุกต้นเดือนตามเวลาไทย ไฟล์ที่ระบบแยกเป็นหลายใบภายหลังอาจทำให้เกินโควตาได้เล็กน้อย

ส่งซ้ำอย่างปลอดภัย (Idempotency-Key)

เครือข่ายหลุดระหว่างส่งไฟล์ อาจไม่รู้ว่าเราได้รับไฟล์แล้วหรือยัง ใส่หัวข้อ Idempotency-Key ที่ไม่ซ้ำกันต่อไฟล์ (เช่น UUID) แล้วใช้ค่าเดิมทุกครั้งที่ส่งไฟล์นั้นซ้ำ

  • key เดิม + ไฟล์และช่องเดิม ภายใน 24 ชั่วโมง: ได้คำตอบแรกกลับมา พร้อมหัวข้อ Idempotent-Replayed: true ไม่สร้างเอกสารใหม่และไม่คิดเงินซ้ำ (ดูสถานะล่าสุดด้วย GET /documents/{id})
  • key เดิมแต่ไฟล์หรือช่องต่างไป: 409 idempotency_conflict
  • คำขอแรกยังรับไฟล์อยู่: 409 idempotency_in_progress พร้อม Retry-After
  • คำขอที่ถูกปฏิเสธก่อนสร้างเอกสาร (เช่น 413 415 402) ไม่จอง key ไว้ แก้แล้วส่งด้วย key เดิมได้
  • key ยาว 1–255 ตัวอักษร ASCII ที่พิมพ์ได้ และแยกกันต่อ API key

PDF ที่มีรหัสผ่าน

สเตทเมนต์จากธนาคารมักเป็น PDF ที่ตั้งรหัสผ่าน ระบบไม่รับ PDF ที่มีรหัสผ่าน (ได้ 422 pdf_encrypted และไม่คิดเงิน) ให้ปลดรหัสผ่านในระบบของคุณก่อนส่ง และไม่ต้องส่งรหัสผ่านให้เรา

# qpdf (Windows, macOS, Linux)
qpdf --decrypt --password='รหัสผ่านของไฟล์' locked.pdf unlocked.pdf

# Python (pip install pikepdf)
import pikepdf
with pikepdf.open("locked.pdf", password="รหัสผ่านของไฟล์") as pdf:
    pdf.save("unlocked.pdf")

ไฟล์ที่ได้ยังเป็นเอกสารเดิมทุกหน้า แค่ไม่มีรหัสผ่าน สเตทเมนต์ที่ยาวเกิน 8 หน้าให้แยกเป็นหลายไฟล์ เช่น qpdf in.pdf --pages . 1-8 -- part1.pdf

ตัวอย่างโค้ด

curl

# ส่งสเตทเมนต์ (คิดเงินหน้าละ 1 หน่วย) ให้อยู่ใต้ลูกค้ารายหนึ่ง แล้วรอผล
curl https://heybuzzai.com/api/v1/documents \
  -H "Authorization: Bearer $BUZZAI_API_KEY" \
  -F "[email protected]" \
  -F "type=bank-statement" \
  -F "client_id=7f0e6d5c-4b3a-4291-8f7e-6d5c4b3a2918" \
  -F "wait=true"

# ดูผลของเอกสาร
curl https://heybuzzai.com/api/v1/documents/b1c3e1d2-4a5b-4c6d-8e9f-0a1b2c3d4e5f \
  -H "Authorization: Bearer $BUZZAI_API_KEY"

# เอกสารที่พร้อมใช้หรือยืนยันแล้ว ที่เปลี่ยนแปลงตั้งแต่วันที่ 1 ตุลาคม
curl "https://heybuzzai.com/api/v1/documents?status=ready,confirmed&since=2026-10-01&limit=100" \
  -H "Authorization: Bearer $BUZZAI_API_KEY"

# เพิ่มลูกค้า
curl https://heybuzzai.com/api/v1/clients \
  -H "Authorization: Bearer $BUZZAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "บริษัท ลูกค้า จำกัด", "tax_id": "9915556123450"}'

# การใช้งานเดือนนี้
curl https://heybuzzai.com/api/v1/usage -H "Authorization: Bearer $BUZZAI_API_KEY"

Python (requests)

import os
import time
import uuid

import requests

API = "https://heybuzzai.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BUZZAI_API_KEY']}"}


def send(path, doc_type="accounting-document", client_id=None):
    """ส่งไฟล์ 1 ไฟล์ แล้วคืนรายการเอกสารที่อ่านได้ (ไฟล์เดียวอาจมีหลายใบ)"""
    key = str(uuid.uuid4())  # 1 ค่าต่อ 1 ไฟล์ ใช้ค่าเดิมทุกครั้งที่ส่งไฟล์นี้ซ้ำ
    data = {"type": doc_type, "wait": "true"}
    if client_id:
        data["client_id"] = client_id
    for attempt in range(5):
        with open(path, "rb") as f:
            r = requests.post(
                f"{API}/documents",
                headers={**HEADERS, "Idempotency-Key": key},
                files={"file": (os.path.basename(path), f)},
                data=data,
                timeout=90,
            )
        busy = r.status_code == 409 and r.json()["error"]["code"] == "idempotency_in_progress"
        if r.status_code in (429, 500, 503) or busy:
            time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
            continue
        if r.status_code >= 400:
            error = r.json()["error"]
            raise RuntimeError(f"{error['code']}: {error['message']}")
        return r.json()["data"]
    raise RuntimeError("ส่งไม่สำเร็จ ลองใหม่ภายหลัง")


def wait_until_read(doc_id):
    """ถามสถานะทุก 3 วินาทีจนอ่านเสร็จ"""
    while True:
        r = requests.get(f"{API}/documents/{doc_id}", headers=HEADERS, timeout=30)
        r.raise_for_status()
        doc = r.json()
        if doc["status"] != "processing":
            return doc
        time.sleep(3)


for doc in send("IV2026-00123.pdf"):
    if doc["status"] == "processing":
        doc = wait_until_read(doc["id"])
    if doc["status"] in ("ready", "confirmed"):
        d = doc["data"]
        print(d["document_number"], d["issuer"]["name"], d["amounts"]["total"])
    elif doc["status"] == "in_review":
        print("รอคนตรวจทาน:", [i["code"] for i in doc["checks"]["issues"]])
    else:
        print(doc["status"], doc["error"])

JavaScript (fetch)

// Node.js 18 ขึ้นไป (มี fetch, FormData และ Blob ในตัว)
import { readFile } from 'node:fs/promises';
import { basename } from 'node:path';

const API = 'https://heybuzzai.com/api/v1';
const headers = { Authorization: `Bearer ${process.env.BUZZAI_API_KEY}` };

async function send(path, type = 'accounting-document') {
  const form = new FormData();
  form.append('file', new Blob([await readFile(path)]), basename(path));
  form.append('type', type);
  form.append('wait', 'true');
  const res = await fetch(`${API}/documents`, {
    method: 'POST',
    headers: { ...headers, 'Idempotency-Key': crypto.randomUUID() },
    body: form,
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message_en}`);
  return body.data; // ไฟล์เดียวอาจได้หลายเอกสาร
}

async function listReady(since) {
  const docs = [];
  let cursor = null;
  do {
    const query = new URLSearchParams({ status: 'ready,confirmed', since, limit: '100' });
    if (cursor) query.set('cursor', cursor);
    const res = await fetch(`${API}/documents?${query}`, { headers });
    if (res.status === 429) {
      await new Promise(r => setTimeout(r, Number(res.headers.get('Retry-After')) * 1000));
      continue;
    }
    const page = await res.json();
    docs.push(...page.data);
    cursor = page.next_cursor;
  } while (cursor);
  return docs;
}

for (const doc of await send('statement-2026-09.pdf', 'bank-statement')) {
  console.log(doc.id, doc.status, doc.units, doc.data?.balances);
}
console.log((await listReady('2026-10-01')).length);

OpenAPI

คำอธิบาย API แบบเครื่องอ่านได้ (OpenAPI 3.1) อยู่ที่ /openapi.json นำไปสร้าง client หรือเปิดในเครื่องมืออย่าง Postman และ Swagger UI ได้

มีคำถามหรือต้องการ endpoint เพิ่ม อีเมลมาที่ [email protected] เรื่องการดูแลข้อมูลดู นโยบายความเป็นส่วนตัว