D
Dehuyz Gateway
Docs Errors

Errors

Semua endpoint API publik return JSON error dengan format konsisten:

{
  "detail": "Human-readable message",
  "code": "stable_machine_code",
  "request_id": "ab12cd34"
}
  • detail: pesan untuk debugging / display ke developer (jangan tampilkan ke end-user mentahan, paraphrase aja).
  • code: stable string yang bisa lo switch on untuk handling.
  • request_id: 16-char hex untuk debug. Sertakan kalau lapor bug ke admin.

HTTP status codes

Status Code Arti
200 / 201 Success
400 bad_request / validation_error Request malformed atau field invalid
401 unauthorized API key kosong atau invalid
403 forbidden Akun suspended, role insufficient
404 not_found Invoice / endpoint gak ditemukan
409 conflict (Future) duplicate idempotency dengan payload beda
422 validation_error Pydantic validation fail (pesan detail di field errors)
429 rate_limit_exceeded Too many requests, lihat header Retry-After
500 internal_error Server crash, lapor admin dengan request_id
502 upstream_error BRI portal unreachable / error
503 service_unavailable (Future) maintenance mode

Validation errors

Field-level errors dari Pydantic:

{
  "detail": "Validation failed",
  "code": "validation_error",
  "errors": [
    {
      "loc": ["body", "amount"],
      "msg": "Input should be greater than 0",
      "type": "greater_than"
    }
  ],
  "request_id": "ab12cd34"
}

Pattern: handle di Python

import httpx

class GatewayError(Exception):
    def __init__(self, code: str, detail: str, status: int):
        self.code = code
        self.detail = detail
        self.status = status
        super().__init__(f"[{code}] {detail}")


def call(method, path, **kwargs):
    r = httpx.request(method, f"{API}{path}", **kwargs)
    if 200 <= r.status_code < 300:
        return r.json()

    try:
        body = r.json()
    except ValueError:
        body = {"detail": r.text, "code": "unknown"}

    raise GatewayError(
        code=body.get("code", "unknown"),
        detail=body.get("detail", "Unknown error"),
        status=r.status_code,
    )


try:
    inv = call("POST", "/api/v1/invoices", json={"amount": 50000})
except GatewayError as e:
    if e.code == "rate_limit_exceeded":
        retry()
    elif e.code == "unauthorized":
        rotate_key()
    elif e.code == "upstream_error":
        # BRI portal lagi error — fallback ke method lain atau retry
        ...
    else:
        log_to_sentry(e)
        raise

Pattern: handle di Node.js

async function call(method, path, opts = {}) {
  const r = await fetch(`${API}${path}`, { method, ...opts });
  const body = await r.json().catch(() => ({}));

  if (r.ok) return body;

  const err = new Error(body.detail || 'Unknown');
  err.code = body.code || 'unknown';
  err.status = r.status;
  err.requestId = body.request_id;
  throw err;
}

try {
  const inv = await call('POST', '/api/v1/invoices', {
    headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount: 50000 }),
  });
} catch (e) {
  if (e.code === 'rate_limit_exceeded') { /* backoff */ }
  else if (e.code === 'unauthorized') { /* rotate key */ }
  else throw e;
}

Lapor bug

Kalau lo nemu bug atau response gak konsisten, kirim:

  1. request_id (dari error response atau header X-Request-Id)
  2. Endpoint + method
  3. Sample request body (mask data sensitif)
  4. Expected vs actual response

Admin bisa cari log dengan request_id itu.