Skip to main content
Once money moves, the question changes from “does this work” to “what happened to this specific payment”. Your system has to answer that without a call to support. This guide covers what to record and how to connect the records so that one query returns the whole story.

The three identifiers

Every trace hangs on three values. Record all three, on every step. The reference ties your record to ours. The correlation_id appears in every error body and identifies one request in our systems. The event_id distinguishes a first delivery from a retry.
A log line with a message but no identifier is close to useless during an incident. Attach the reference to every line you write about a transaction.

What to record

Log five things:
  • Every request you send, with its reference and the response status.
  • Every webhook you receive, with its event name, event id, and full payload.
  • Every quote you create, with its rate and expires_at.
  • Every error, with its code and correlation_id.
  • Every state change, appended rather than overwritten.

Append, do not overwrite

Store status changes as new rows, not as an updated field. An overwritten field tells you where a payment is. An append-only log tells you how it got there and how long each stage took. Only the second answers a dispute.

Redact before you log

Never write these to a log, a dashboard, or an error tracker:
  • Your CLIENT_SECRET.
  • The X-Auth-Signature header.
  • Full card numbers or card security details.
Log the CLIENT_ID and the correlation_id instead. Both identify the caller without exposing the credential. See API keys and credential security.

Trace one transaction end to end

Transaction Flow A complete trace covers each step:
  1. The user’s request into your system.
  2. The quote you created, and the rate you displayed.
  3. The request you sent to Bitnob, and its response status.
  4. Each webhook you received, and whether you processed it.
  5. The final state, and when you told the user.
Server-side, resolve a single record with Get transaction by id or reference. Sweep a period with Get all transactions.

Reconcile on a schedule

Webhook retries are finite. An endpoint that is down long enough misses events permanently, and no amount of logging recovers what you never received. Run a scheduled job that compares our record against yours:
1

List recent transactions

Query Get all transactions for the period since your last run.
2

Match on reference

Join our records to yours by reference.
3

Flag the mismatches

Anything complete on our side and pending on yours is a missed delivery. Anything pending on our side past its expected window needs a human.
4

Repair and record

Apply the correct state, and log that reconciliation made the change rather than a webhook.

Alert on the right signals

Alert on conditions that mean money is stuck, not on raw error counts. Send them somewhere a person reads. A message to a chat channel beats a dashboard nobody opens. Metrics tools such as Datadog, Prometheus, or Grafana handle the counting. Log tools such as CloudWatch or Logtail handle the search. Either is enough to start.

Build an internal lookup tool

Support questions arrive as “where is my money”, not as a reference. Build one internal page that lets your team:
  • Search by reference, customer, or amount.
  • See the full status history.
  • See the raw request and response.
  • Replay a failed webhook.
  • Mark a case resolved, with a note.
This is usually the highest-leverage internal tool a payments integration has, and it takes a day to build.

Show users their own trace

Expose a reduced version to the user: the status, the amount they sent, the rate they got, the timestamps, and what happens next if it is stuck. Most support tickets are requests for information the user could have read themselves.

Next

Errors and retries

Status codes, the error body, and the retry rules.

Test in sandbox

Produce the failures you want to be ready for.