Scenarios are configuration, not verification data. They live on your account, are shared by every teammate using a sandbox key, and have no effect in production.
How it works
1
Create a scenario
POST /v1/sandbox/scenarios/ with the login your user will type, a payload of the data to return, and optionally how the task should behave — status, delay, multi-factor authentication (MFA).2
Run a verification
Start a normal sandbox verification. In Truv Bridge, select Truv Payroll Provider and sign in with the username and password you gave the scenario. If your scenario sets
provider_id or company_domain, use a connection that matches those values.3
Get your data back
Truv resolves the scenario and serves your payload as the task result. Tasks, webhooks, and reports behave exactly as they do for any other connection.
Create a scenario [Server-side]
A scenario is one flat object: what it matches on, how it behaves, and what it returns. The endpoints accept sandbox keys only — a production key gets403 env_not_sandbox.
This request creates a scenario with one salaried employment and one paystub:
Use it in Bridge
Start a sandbox verification the way you normally do. In Truv Bridge, select Truv Payroll Provider and sign in withacme.salaried / s4ndb0x. The task completes with your data instead of Truv’s built-in test data. Read the result the same way as for any other connection: from the task webhooks and the income and employment report.
The scenario above sets neither provider_id nor company_domain, so any payroll provider works in sandbox. If your scenario sets either field, use a connection that matches those values.
Full sample: profile, bank account, and two paystubs with year-to-date totals
Full sample: profile, bank account, and two paystubs with year-to-date totals
This request fills every section of an employment. It creates a separate scenario,
acme.full, so it doesn’t collide with the one above.The response
The response is the stored scenario, with the fields you left out filled in with their defaults. The request sent no behavior fields, so the task completes withdone, with no delay and no MFA. The payload is abbreviated here; the API returns it in full.
filenames, ssn_last4, date_of_birth, and phone are matching fields for other data sources, so they stay empty on a payroll scenario.
Always send
"data_source": "payroll". username and password are supported on payroll, financial accounts, and insurance, so Truv can’t infer the data source from them and refuses the request with data_source_required.Scenario fields
Identity and routing
How a scenario is selected
These fields decide which user input the scenario answers. A field you leave out narrows nothing, so it matches any value.
No two of your payroll scenarios can have the same matching fields — the API refuses a duplicate and names the scenario that already has them.
enabled and products don’t make two scenarios distinct: change a matching field, or edit the existing scenario instead.
Behavior
payload is required unless behavior_status is a failure status. A scenario that only ever fails carries no data.
See Task lifecycle for what each status means to your integration.
Payload reference
Thepayload object contains an employments array and optional flags. The tables below describe the fields in each employment record. Every field is optional unless noted — send only what your test needs, and leave the rest out.
employments[]
employments[]
employments[].profile
employments[].profile
employments[].company
employments[].company
employments[].paystubs[]
employments[].paystubs[]
employments[].bank_accounts[]
employments[].bank_accounts[]
employments[].w2s[]
employments[].w2s[]
employments[].shifts[]
employments[].shifts[]
flags
flags
Fraud markers reported alongside the data, at the top level of the payload next to
employments.Money and quantities are decimal strings, so nothing is lost to floating point:
"4615.38", not 4615.38.Examples
Each example is a request body forPOST /v1/sandbox/scenarios/ and creates a new scenario. Send it with the same curl command as the first scenario. The hourly, two-job, terminated, and failing-login bodies are complete. The rest are abbreviated to the fields the example is about, and each one says so above its code block.
Hourly employee with overtime
Express an hourly wage asincome plus income_unit: "HOURLY", and put the hours on the paystub.
Two employments
Return a second job by adding another entry toemployments. Up to 10 fit in one scenario.
Terminated employment
Setend_date and is_active: false. end_date can equal start_date but not precede it.
Failing login
A failure status needs no payload. Use it to exercise the error paths in your integration.Slow task
Delay the task before it settles, up to 120 seconds, to test loading states and webhook ordering. Abbreviated: replace theemployments placeholder with complete employment objects before sending this request.
MFA challenge
behavior_mfa.challenges replays one screen per entry, in order. A TEXT challenge prompts for a code; an OPTIONS challenge renders a select. A challenge with neither answer nor answers accepts anything the user types.
Abbreviated: replace the employments placeholder with complete employment objects before sending this request.
answers when more than one response is acceptable. Up to 5 challenges per scenario.
Refresh-only data
Author the state a data refresh should find by pinning a scenario to refreshes withis_refresh: true. On a refresh it wins over the scenario it shadows, because it sets is_refresh and the other doesn’t. Create it alongside the acme.salaried scenario — don’t update that one.
Abbreviated: replace the employments placeholder with complete employment objects before sending this request.
acme.salaried and gets the original data; the refresh of that same connection gets the raise.
Pin a scenario to one provider or employer
Addprovider_id or company_domain to answer only connections that went through a specific provider or employer. This is how the same login returns different data depending on where the user connected. Create it alongside the acme.salaried scenario — the extra matching field makes it more specific, so it wins for ADP connections.
company_domain matches the employer the user selects through company search in Truv Bridge — the domain that Search companies returns. A user who picks a provider directly, such as Truv Payroll Provider, selects no employer, so a scenario that sets company_domain doesn’t match their connection.
Abbreviated: replace the employments placeholder with complete employment objects before sending this request.
Which scenario wins
Several scenarios can share a login on purpose. A refresh-only or provider-pinned scenario shadows a general one for the same username. When more than one scenario matches, Truv selects the scenario with more matching fields (username, password, provider_id, company_domain). is_refresh is supported on every data source, but it doesn’t count toward matching fields. If there is still a tie, a scenario that sets is_refresh wins over one that doesn’t. After that, Truv selects the oldest scenario.
Disabled scenarios never take part, and a scenario pinned to products is skipped for tasks of any other product. If nothing matches, the task falls through to Truv’s built-in test data.
Check the winner without running a verification
POST /v1/sandbox/scenarios/resolve/ is a dry run: send the input a user would supply and get back the scenario that would answer it.
scenario is abbreviated here; the API returns the full scenario. matched_on lists the fields the winning scenario actually constrained. A 404 means no scenario of yours matches and the verification would get Truv’s built-in test data.
Manage scenarios
List and filter
{"results": [...], "next": "..."}. Follow next until it is null. Results come back oldest first. Matching fields depend on the input, so use the resolve endpoint to see the actual winner.
Update
PATCH (or PUT — they behave identically) replaces every top-level key you send and leaves the rest untouched. payload is replaced wholesale, so send the complete object, not a fragment.
data_source after creating a scenario. To use another data source, create a new scenario with that source and a compatible payload.
Delete
Attach PDFs
A paystub or W-2 can carry a real file. Upload it to your sandbox document library once, then reference it by id from as many scenarios as you like. A paystub’s file is optional; every W-2 needs one, because a W-2 with nothing behind it is dropped when the task is served.1
Upload the paystub PDF
Send the file as The response is the library entry. Keep the top-level
multipart/form-data. filename is a separate field — it is the name the entry is addressed by, and it does not have to match the name on disk.id — that is the document_id your payload references.documents is test metadata you configure for the file, not data extracted from it. You didn’t send it, so the entry got the default: one paystub for John Doe at Acme Inc. Only document-upload scenarios read it — a payroll scenario serves the file and ignores it, so leave it out.2
Upload the W-2 PDF
The same endpoint also takes a JSON body, with the bytes base64-encoded in The response has the same shape as the multipart one.
content. Use whichever transport suits your client — send exactly one of file or content, never both.validations and documents are abbreviated here.3
Reference both from a scenario
Put each Sign in with
id on the record it belongs to: the paystub’s file on the paystub, the W-2’s file on the W-2.acme.documents / s4ndb0x and the completed task carries both PDFs, downloadable through the same endpoints as any other connection: Pay Statements for the paystub and Tax Documents for the W-2.Managing the library
GET /v1/sandbox/documents/ lists your entries and DELETE /v1/sandbox/documents/{id}/ removes one. A delete is refused with 409 resource_in_use while any scenario still references the file, so drop the reference first. One upload serves any number of scenarios — reference the same document_id from all of them rather than uploading the file again.
Filenames are unique per account, compared without regard to case, and Truv’s own documented sandbox filenames are reserved.
Limits
These are the shipped defaults. Contact Truv Support if your test plan needs more headroom.Errors
Sandbox configuration errors use the standard error envelope:Next steps
Income & Employment Testing
Truv’s built-in payroll credentials and sample reports
Task lifecycle
What each
behavior_status means to your integrationData refresh
How refreshes reach your
is_refresh scenariosSandbox API reference
Full request and response schemas for every sandbox endpoint
Launch checklist
What to verify before you switch to production keys