VRPlatformVRPlatform

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=true

dryRun 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: false
  • X-VRPlatform-Audit-Action-Id is 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.

  1. Apply documented deterministic rules locally.
  2. Read the current entity and field-level lock metadata.
  3. Send the exact proposed request with dryRun=true before confirmation.
  4. Render structured error code, issues, and context fields.
  5. Discard provisional IDs from the response.
  6. Send the same payload without dryRun after confirmation.
  7. 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.

On this page