Dry runs
Send dry_run=true and a write is fully validated but never persisted. You get the response body you would have received; the database is untouched and no side effects fire.
Either form works:
What is actually checked
A dry run is not a schema check. It runs the same rules the real call runs, including the ones that need to look at your data:
- required and conditionally-required fields
- value constraints and allowed values
- referenced records exist and belong to your institution — award cycles, funds, donors, assigned users
- unrecognised field names
- uniqueness, such as an email already in use
So a dry run that succeeds is strong evidence the real call will succeed. That is the point: it is worth more than a validation you write yourself, because it is the same code path.
Reading the response
You get the resource you would have created, with one tell: on a create, the returned id is 0. The record was never written, so no identifier was ever assigned.
Never store an id from a dry run, and never treat 0 as a real record. If you are checking whether a create succeeded by testing for a non-zero id, a dry run correctly reads as “did not create anything”.
Idempotency keys are not consumed
A dry run leaves an Idempotency-Key unused. You can validate a request and then send the same key for real. This makes the natural pattern safe:
- Dry-run the request to confirm it validates.
- Send it for real with an
Idempotency-Key. - Retry on failure with the same key.
Why this matters more here than elsewhere
There is no sandbox environment and no test-mode key, so there is nowhere to practise a write harmlessly. Dry runs are the closest thing available: they are the only way to exercise a real write path, against real data, without changing anything.
Use them for anything you have not sent before — a new integration, a changed payload shape, or a bulk operation where the first record failing tells you far more cheaply than the hundredth.