Dry Run Mode
Validate supported mutations through the real business path without persistence
Dry run mode lets a client validate a supported mutation through the same synchronous business and locking path as a real write, then rolls the database transaction back.
Add the query parameter to the normal mutation endpoint:
POST /transactions?dryRun=truedryRun is a query parameter, never a request-body field.
Discover Supported Endpoints
Check the generated OpenAPI operation: an endpoint supports dry run when it
exposes the optional boolean dryRun query parameter.
Query parameters are strict. Sending dryRun to an endpoint that does not
declare it returns 400 BAD_REQUEST; it is not silently ignored.
Supported families currently include database-backed mutations for:
- accounts and banking initialization
- bank accounts, bank records, and saved bank-rule execution
- contacts
- general-ledger imports and opening balances
- listings, ownership periods, and listing groups
- recurring fees and recurring-fee listing periods
- reservations and adjustments
- owner statements and statement payouts
- tax rates
- team initialization
- transactions and recurring transaction templates
Use the OpenAPI operation rather than this family summary when deciding whether to display a dry-run action.
What A Dry Run Does
A supported dry run:
- parses the normal request schema
- enforces permissions and status rules
- executes repository validation
- evaluates books-closed, statement-period, statement-attachment, reconciliation, and row locks
- performs the normal database write path inside a transaction
- runs synchronous response mapping
- returns the normal success or error shape
- rolls the database transaction back before completion
Dry run is a safety mechanism, not a cheaper validation path. Imports and other large mutations can take similar time and acquire similar database locks.
What Does Not Persist
A dry run does not leave:
- created or updated database rows
- audit actions, mutations, or effects
- queued repository work
- tracking or analytics events
- webhooks, email, or notifications
- storage or third-party writes
Supported routes are limited to operations where this guarantee actually holds.
Response Contract
Dry run returns the endpoint's normal response schema and status. It does not
add a wouldApply wrapper.
Generated IDs, unique references, and other created values are provisional. They refer to rows that were rolled back and must not be stored or reused.
Supported dry-run responses use these audit headers:
X-VRPlatform-Audit-Should-Poll: falseX-VRPlatform-Audit-Action-Idis omitted
Example
curl 'https://api.vrplatform.app/transactions?dryRun=true' \
-X POST \
-H 'content-type: application/json' \
-H 'x-api-key: your-api-key' \
-H 'x-team-id: your-team-id' \
--data '{
"type": "expense",
"date": "2026-07-14",
"description": "Repair invoice",
"contactId": "11111111-1111-4111-8111-111111111111",
"lines": [
{
"description": "Repair",
"accountId": "22222222-2222-4222-8222-222222222222",
"listingId": "33333333-3333-4333-8333-333333333333",
"party": "owners",
"amount": -10000
}
]
}'On success, the response is a normal transaction object. Its transaction and line IDs are provisional.
Recommended UI Flow
- Apply documented deterministic rules locally.
- Read the current entity and field-level lock metadata.
- Send the exact proposed request with
dryRun=truebefore confirmation. - Render structured error
code,issues, andcontextfields. - Discard provisional IDs from the response.
- Send the same payload without
dryRunafter confirmation. - Handle a real-write failure because state can change after the dry run.
Do not treat a successful dry run as a reservation or lock on future state.
Recurring Template Runs
POST /transactions/recurring-templates/{id}/run?dryRun=true returns the
provisional transactions that the run intends to create.
When dates=null, the API calculates due dates and creates those provisional
instances inline so the response is useful, while the surrounding transaction
still rolls everything back.
Dry Run Versus Preview
Use dry run when the normal mutation response is useful and database rollback is sufficient.
Use a dedicated /preview endpoint when callers need projected consequences
that are not present in the normal response. Preview responses intentionally
differ from their sibling mutation response.
Generic dry run is intentionally unavailable for:
- books closing
- reservation journal regeneration
- transaction journal regeneration
- reservation line-mapping changes
- transaction line-mapping changes
These operations need consequence-oriented results such as affected entities, posting inserts, updates, deletes, skips, lock failures, and cent deltas.
Operations whose primary behavior is external are also excluded, including uploads, OAuth, Plaid, connection and sync actions, invitations, email, and other provider calls. Database rollback cannot undo them.
