Skip to main content
Author your own payroll test data in sandbox. Create a scenario that pairs a login with the employment, paystub, W-2, and bank account data you want back, then sign in with that login in Truv Bridge to get it. Your scenarios sit alongside Truv’s built-in test data — nothing you create replaces or hides it. Use this when the shipped credentials don’t cover the case you need to test: a specific pay frequency, a terminated employment, a second job, an income figure your decision engine sits right on the edge of. For the full request and response schemas, see the Sandbox API reference.
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 gets 403 env_not_sandbox.
Truv’s built-in test credentials are reserved. For payroll scenarios, don’t use goodlogin or error.user, whatever the password. Truv checks reserved credentials when you create or update a scenario and returns reserved_credential on a conflict.The reserved set grows as Truv publishes new test credentials. Use a distinctive username prefix, such as acme., to reduce the risk of a conflict. Other data sources reserve their own built-in test data, listed under Test Credentials and Test Documents.
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 with acme.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.
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 with done, with no delay and no MFA. The payload is abbreviated here; the API returns it in full.
This guide covers payroll scenarios. 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.
Set at least one of username, password, provider_id, or company_domain. The API refuses a scenario that sets none of them, because it would answer every payroll verification in your sandbox. is_refresh doesn’t count on its own.

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

The payload 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.
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 for POST /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 as income plus income_unit: "HOURLY", and put the hours on the paystub.

Two employments

Return a second job by adding another entry to employments. Up to 10 fit in one scenario.

Terminated employment

Set end_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 the employments 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.
Use 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 with is_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.
The user signs in once with acme.salaried and gets the original data; the refresh of that same connection gets the raise.

Pin a scenario to one provider or employer

Add provider_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

The response is cursor-paginated — {"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.
You can’t change 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 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.
The response is the library entry. Keep the top-level 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 content. Use whichever transport suits your client — send exactly one of file or content, never both.
The response has the same shape as the multipart one. validations and documents are abbreviated here.
3

Reference both from a scenario

Put each id on the record it belongs to: the paystub’s file on the paystub, the W-2’s file on the W-2.
Sign in with 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 integration

Data refresh

How refreshes reach your is_refresh scenarios

Sandbox API reference

Full request and response schemas for every sandbox endpoint

Launch checklist

What to verify before you switch to production keys