Dry Runs

Dry runs #

Any request can be made a dry run by sending a rollback header. The API validates the request and does the work in full, and returns the response it would have returned, including the ids of anything it created. Then it discards every change. Use it to check that a write is accepted before making it for real.

curl -X POST https://api.microbooks.io/books/v1/transaction \
  -H "Authorization: Bearer $TOKEN" \
  -H "rollback: 1" \
  -H "Content-Type: application/json" \
  -d '{"transaction_type": "IN", "account_id": 12, "narration": "Consulting, March", "transaction_date": "2026-03-31", "line_items": [{"account_id": 40, "amount": 1200, "quantity": 1}]}'

The response carries X-Dry-Run: true, so a client can confirm that nothing was saved.

Header values #

ValueEffect
1, true, yes, any other value, or emptyDry run: nothing is saved
0, false, no, off (any case)Normal request: the work is saved
Header absentNormal request

What a dry run covers #

  • Every endpoint. The header works on any request, including posting a transaction.
  • The response is real. The ids it returns are what a real request would have created. They do not exist afterwards.
  • Errors behave as usual. The same validation and bookkeeping rules apply, so a dry run that fails tells you exactly why the real request would fail.
  • Not idempotent. A dry run is never stored against an Idempotency-Key, so sending the same key without the header afterwards runs the request for real.
Card endpoints refuse dry runs
POST /subscription/card-setup and DELETE /subscription/card call the payment service, which a rollback cannot undo. With a rollback header they return 422 with errors.rollback, rather than half-honouring it.

Errors never save partial work #

Independently of the header, any request that ends in an error (4xx or 5xx) is rolled back completely. A transaction whose line items fail validation, for example, does not leave its header row behind.