Errors & idempotency

One error envelope, stable codes, and idempotency that actually protects money.

Not live yet — api.baynoy.com does not resolve, so the examples below will not run today. These shapes are settled: design against them now and nothing changes when keys go live.

One error envelope

Every non-2xx response carries {"error": {"code", "message", "details?"}}. Branch on code — it is a stable identifier. Never branch on message: messages are for humans and they change.

Node
try {
  await baynoy.payouts.create({ amount: 250000, currency: "EUR", beneficiary: "ben_123" },
    { idempotencyKey: key });
} catch (err) {
  switch (err.code) {
    case "insufficient_funds":       return showTopUp();
    case "beneficiary_cooldown":     return showCooldown(err.details.availableAt);
    case "idempotency_key_reused":   return; // already done — do nothing
    case "rate_limited":             return retryAfter(err.details.retryAfterMs);
    default: throw err;              // unknown codes are bugs, not flows
  }
}

Idempotency is not optional on money

Financial POSTs require an Idempotency-Key scoped to your account and endpoint. The same key with the same body returns the first result; the same key with a different body is a conflict. Generate the key before the first attempt and reuse it for every retry of that same intent.

cURL
curl https://api.baynoy.com/v1/payouts \\
  -H "Authorization: Bearer bk_test_..." \\
  -H "Idempotency-Key: 7c1f0b6e-..." \\
  -d amount=250000 -d currency=EUR -d beneficiary=ben_123

# same key + same body  -> the original result, no second payout
# same key + other body -> 409 idempotency_key_reused

Retries and rate limits

Retry 5xx and rate_limited with exponential backoff and jitter; never retry a 4xx other than rate_limited without changing something. Rate-limit responses carry the wait time in details — respect it rather than guessing.