humanblnk tutorial

Chapter 12 · Running it

Errors and retries

humanblnk's errors are its own; when Blnk refused the call they also wrap Blnk's, for your logs. A Blnk error humanblnk has no name for comes back as a *blnk.APIError, and VerifyWebhook returns blnk.ErrBadSignature or blnk.ErrStale when the check fails. Always compare with errors.Is, never on the message.

What each error means

ErrorMeansWhat to do
humanblnk.ErrInsufficientFundsthe source account can't cover ittell the customer; nothing moved
humanblnk.ErrDuplicatethis exact write already happened, appliedtreat as success; the existing entry comes back with it
humanblnk.ErrReferenceUsedthis write didn't apply: the reference holds a different transaction, or this one was refused partwayafter a refusal, record again under a new reference; otherwise investigate how two writes got the same reference
humanblnk.ErrNotFoundno such account, entry or holdcheck the id
humanblnk.ErrUnbalancedFrom and To don't add up to the same amountfix the postings
humanblnk.ErrAlreadyReversednothing (or not that much) is left to reversenothing to do; pass a Ref to get your own reversal back on a retry
humanblnk.ErrHoldSettledthe hold was already captured, cancelled or expiredlook at what happened before acting again
humanblnk.ErrInvalidrefused before reaching Blnk: missing reference, negative amount, wrong ledger, …a bug in the caller; don't retry
humanblnk.ErrReadBackRecordAll applied, but reading the entries back faileddon't record it again; look the entries up by reference
money.ErrCurrencyMismatchan amount's currency doesn't match the account'sa bug in the caller
money.ErrUnknownCurrencya Money that was never set, or an unknown codea bug in the caller

The blnk client already retries transient failures (3 times by default), and only where a repeat is harmless. After that, a *blnk.APIError with a 4xx status means Blnk refused the call. A network error or a gateway timeout means the outcome is unknown: retry with the same arguments if the table below says the call is safe to repeat.

What is safe to repeat

CallSafe to repeat?Because
Transactions.Record, RecordAllyes, with the same Refa used reference is checked: ErrDuplicate if it happened
Transactions.Reverseyes, with the same Refreturns the reversal already made; without a Ref, a full reversal says ErrAlreadyReversed once done
Holds.Placeyes, with the same transactionreturns the same hold, captured or not; ErrHoldSettled if it was released
Holds.Capture (full)yesfinds the earlier capture
Holds.Capture (partial)wait a few seconds firstBlnk can't tell a repeated partial capture still being processed from a new one
Holds.Cancelyes
Accounts.OpenForCustomeryesone account per owner, ledger and currency
Identities.Createnouse FindByCustomer first
Accounts.CreateInternal, Ledgers.Createnolook with List first
Watches.Create, Watches.Changenolook with List first; Change makes a new watch each time
Identities.UpdateMetayesit merges the keys
Ledgers.Rename, Reconciliation.ChangeRuleyesthey replace, not add
Watches.Delete, Reconciliation.RemoveRuleyesa repeat may return ErrNotFound
Reconciliation.Upload, AddRule, Run, RunLinesnoeach call makes a new statement, rule or run
every read (Get, List, GetByReference, Accounts.System, Preview)yes

References, in one rule

Build every reference from your own ids, with humanblnk.Reference:

CallReference
humanblnk.Reference("deposit", "paystack", paystackRef)deposit:paystack:ps_123
humanblnk.Reference("transfer", transferID)transfer:tr_42
humanblnk.Reference("payout", payoutID)payout:po_7

Never use a random value or a timestamp: a retry must produce the same reference, so Blnk can refuse to apply it twice.

When a split or batch fails partway

Blnk moves a split (and a RecordAll batch) one part at a time. humanblnk sends it as a hold and captures it at once, so if a later part fails:

  1. You get the error (say ErrInsufficientFunds), and nothing moved: Blnk released the earlier parts' holds before it answered.
  2. The references stay used. Recording again with the same Ref returns ErrReferenceUsed; use a new one.

Get and GetByReference show the entry as StatusRejected.

If your process stops between the hold and its capture (a crash, a deploy), the money is reserved but hasn't moved. Retry with the same Ref within humanblnk.RecordHold (15 minutes) and the capture finishes. After that the hold expires, the money goes back, and the reference reads as ErrReferenceUsed. Keep Blnk's worker running and monitored: it expires holds.