Developer Docs

    เริ่มต้น

    Partner API — Auth

    Partner API — Sales

    Partner API — Products

    Partner API — Inventory

    Partner API — Purchasing

    Partner API — Customers

    Partner API — Reports

    Partner API — Webhooks

    อ้างอิง

    Scopes & Error Codes

    หน้านี้รวบรวม Scope ทั้งหมดที่ Partner API รองรับ และ Error Code ที่อาจได้รับจากทุก Endpoint


    Scope Catalog

    Scope กำหนดสิทธิ์การเข้าถึง Endpoint ของ Token — ต้องระบุตอนสร้าง API Application

    ScopeEndpoints ที่เข้าถึงได้
    read:salesGET /sales, GET /sales/:id/items
    read:productsGET /products
    read:inventoryGET /inventory, GET /inventory/movements
    read:purchasingGET /purchase-orders, GET /purchase-orders/:id/items, GET /suppliers
    read:customersGET /customers (ไม่รวม PII fields)
    read:customers:piiGET /customers — เพิ่ม PII fields: phone, email, date_of_birth, address, city, state, postal_code, country, tax_id
    read:reportsGET /reports/sales-summary, GET /reports/inventory-summary
    read:webhooksGET /webhooks, POST /webhooks, GET /webhooks/:id, PATCH /webhooks/:id, DELETE /webhooks/:id, GET /webhooks/:id/deliveries, POST /webhooks/:id/ping

    หมายเหตุ

    • Token หนึ่งสามารถมีหลาย Scope ได้
    • read:customers:pii ต้องใช้ร่วมกับ read:customers เสมอ
    • การเพิ่ม Scope ต้องแก้ไข API Application ใน Settings และขอ Token ใหม่

    Error Codes

    Error response ของ Partner API ทั้งหมดเป็น flat object ตาม OAuth2 RFC 6749 §5.2

    {
      "error": "error_code",
      "message": "Human-readable description"
    }
    

    ไม่มีฟิลด์ success — ใช้ HTTP Status Code เป็นตัวบ่งชี้หลัก

    400 Bad Request

    errormessageExtra Fieldสาเหตุ
    invalid_request"Query parameter validation failed"details: [{field, message}]Query หรือ Body ไม่ผ่าน Validation

    401 Unauthorized

    errormessageสาเหตุ
    invalid_token"Access token is required"ไม่ส่ง Authorization header
    invalid_token"Access token has expired"Token หมดอายุ
    invalid_token"Access token is malformed or invalid"Signature ผิด หรือ Token format ไม่ถูกต้อง
    invalid_client"Invalid client_id or client_secret"Credentials ไม่ถูกต้องที่ /oauth/token
    invalid_client"API application is suspended or revoked"Application ถูก Revoke หรือสถานะไม่ใช่ ACTIVE

    403 Forbidden

    errormessageExtra Fieldสาเหตุ
    access_denied"API access is not available on your current subscription plan."Plan ไม่รองรับ Partner API (FREE / BASIC)
    insufficient_scope"Access token is missing required scope(s)"required_scopes: string[]Token ไม่มี Scope ที่จำเป็น
    limit_reached"You have reached the webhook subscription limit (...)"Webhook Subscription เกิน Quota ของ Plan

    404 Not Found

    errormessageสาเหตุ
    not_found"Branch not found"Branch ID ไม่ถูกต้องหรืออยู่นอกสิทธิ์ของ Token
    not_found"Warehouse not found"Warehouse ID ไม่ถูกต้องหรืออยู่นอกสิทธิ์ของ Token
    not_found"Webhook subscription not found"Webhook ID ไม่ถูกต้องหรือเป็นของ Application อื่น

    429 Too Many Requests

    errormessageสาเหตุ
    rate_limited"Rate limit exceeded. Max <N> requests per 15 minutes."เกิน Rate Limit ของ Plan — ดู Retry-After header

    500 Internal Server Error

    errormessageสาเหตุ
    server_error"..."Unexpected server error

    Error Handling Example

    const res = await fetch('/api/partner/v1/sales?limit=50', {
      headers: { Authorization: `Bearer ${accessToken}` },
    });
    
    if (res.status === 401) {
      const body = await res.json();
      if (body.error === 'invalid_token') {
        // Token หมดอายุ — ขอใหม่แล้วลองใหม่
        const newToken = await getNewToken();
        return fetchWithToken(newToken);
      }
    }
    
    if (res.status === 403) {
      const body = await res.json();
      if (body.error === 'insufficient_scope') {
        // body.required_scopes บอกว่าต้องเพิ่ม Scope อะไรใน Application
        throw new Error(`ไม่มีสิทธิ์: ต้องการ ${body.required_scopes?.join(', ')}`);
      }
      if (body.error === 'access_denied') {
        throw new Error('Plan ไม่รองรับ Partner API');
      }
    }
    
    if (res.status === 429) {
      const retryAfter = res.headers.get('Retry-After');
      await sleep(Number(retryAfter) * 1000);
      return retry();
    }
    
    if (!res.ok) {
      const body = await res.json();
      throw new Error(body.message || body.error);
    }
    
    const { data, pagination } = await res.json();