curl --request POST \
--url https://prod.truv.com/v1/sandbox/scenarios/resolve/ \
--header 'Content-Type: application/json' \
--header 'X-Access-Client-Id: <api-key>' \
--header 'X-Access-Secret: <api-key>' \
--data '
{
"data_source": "payroll",
"product": "income",
"username": "acme.salaried",
"password": "s4ndb0x",
"provider_id": "adp"
}
'import requests
url = "https://prod.truv.com/v1/sandbox/scenarios/resolve/"
payload = {
"data_source": "payroll",
"product": "income",
"username": "acme.salaried",
"password": "s4ndb0x",
"provider_id": "adp"
}
headers = {
"X-Access-Client-Id": "<api-key>",
"X-Access-Secret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Access-Client-Id': '<api-key>',
'X-Access-Secret': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
data_source: 'payroll',
product: 'income',
username: 'acme.salaried',
password: 's4ndb0x',
provider_id: 'adp'
})
};
fetch('https://prod.truv.com/v1/sandbox/scenarios/resolve/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://prod.truv.com/v1/sandbox/scenarios/resolve/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'data_source' => 'payroll',
'product' => 'income',
'username' => 'acme.salaried',
'password' => 's4ndb0x',
'provider_id' => 'adp'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Access-Client-Id: <api-key>",
"X-Access-Secret: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://prod.truv.com/v1/sandbox/scenarios/resolve/"
payload := strings.NewReader("{\n \"data_source\": \"payroll\",\n \"product\": \"income\",\n \"username\": \"acme.salaried\",\n \"password\": \"s4ndb0x\",\n \"provider_id\": \"adp\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Access-Client-Id", "<api-key>")
req.Header.Add("X-Access-Secret", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://prod.truv.com/v1/sandbox/scenarios/resolve/")
.header("X-Access-Client-Id", "<api-key>")
.header("X-Access-Secret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"data_source\": \"payroll\",\n \"product\": \"income\",\n \"username\": \"acme.salaried\",\n \"password\": \"s4ndb0x\",\n \"provider_id\": \"adp\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://prod.truv.com/v1/sandbox/scenarios/resolve/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Access-Client-Id"] = '<api-key>'
request["X-Access-Secret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data_source\": \"payroll\",\n \"product\": \"income\",\n \"username\": \"acme.salaried\",\n \"password\": \"s4ndb0x\",\n \"provider_id\": \"adp\"\n}"
response = http.request(request)
puts response.read_body{
"scenario": {
"id": "7b9f0c2a4d6e4f1b8c3a5d7e9f0b1c2d",
"data_source": "payroll",
"label": "Acme - salaried, biweekly",
"description": "Full-time salaried user with two paystubs",
"products": [
"income"
],
"enabled": true,
"username": "acme.salaried",
"password": "s4ndb0x",
"provider_id": null,
"company_domain": null,
"is_refresh": null,
"filenames": [],
"ssn_last4": null,
"date_of_birth": null,
"phone": null,
"behavior_status": "done",
"behavior_error_message": "",
"behavior_duration_seconds": 0,
"behavior_mfa": {
"challenges": [
{
"type": "TEXT",
"label": "Enter the code we sent to your phone",
"answer": "12345"
},
{
"type": "OPTIONS",
"label": "Where should we send your code?",
"options": [
{
"value": "sms",
"label": "Text message"
},
{
"value": "email",
"label": "Email"
}
],
"answers": [
"sms",
"email"
]
}
]
},
"payload": {
"flags": {
"is_fraudulent": false,
"is_suspicious": true
},
"employments": [
{
"profile": {
"full_name": "Jane Roe",
"first_name": "Jane",
"last_name": "Roe",
"ssn": "991-91-9991",
"date_of_birth": "1988-04-12",
"email": "jane.roe@acme.com",
"phone": "4155554193",
"home_address": {
"street": "1 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US"
}
},
"company": {
"name": "Acme Inc.",
"ein": "123456789",
"domain": "acme.com",
"phone": "4155554193",
"address": {
"street": "1 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US"
}
},
"job_title": "Staff Engineer",
"job_type": "F",
"start_date": "2022-03-01",
"end_date": null,
"original_hire_date": "2019-07-08",
"is_active": true,
"manager_name": "Jenny McDouglas",
"income": "124800.00",
"income_unit": "YEARLY",
"income_currency": "USD",
"pay_rate": "60.00",
"pay_frequency": "BW",
"bank_accounts": [
{
"bank_name": "Acme CU",
"account_name": "Everyday",
"account_number": "1234567890",
"routing_number": "111000025",
"account_type": "C",
"deposit_type": "A",
"deposit_value": "500.00"
}
],
"paystubs": [
{
"pay_date": "2026-08-14",
"period_start": "2026-08-01",
"period_end": "2026-08-14",
"currency": "USD",
"gross_pay": "4800.00",
"net_pay": "3312.00",
"gross_pay_ytd": "76800.00",
"net_pay_ytd": "52992.00",
"hours": "80.00",
"basis_of_pay": "S",
"earnings": [
{
"name": "Regular",
"amount": "4800.00",
"rate": "60.00",
"units": "80.00"
}
],
"earnings_ytd": [
{
"name": "Regular",
"amount": "76800.00",
"rate": "60.00",
"units": "1280.00"
}
],
"deductions": {
"Federal Tax": "1056.00",
"Social Security": "297.60",
"Medicare": "69.60",
"State Tax": "64.80"
},
"deductions_ytd": {
"Federal Tax": "16896.00",
"Social Security": "4761.60",
"Medicare": "1113.60",
"State Tax": "1036.80"
},
"document_id": "a41d7e9c05b24f6b8e3c1d5a7f92b604"
}
],
"w2s": [
{
"year": "2025",
"document_id": "3f1c8a90b7d2416e9a5c0e4f2b8d6a17",
"fields": {
"wages": "124800.00"
}
}
],
"shifts": [
{
"start_date": "2026-08-18",
"end_date": "2026-08-18",
"timezone": "America/Chicago",
"time_entries": [
{
"entry_date": "2026-08-18",
"start": "08:00",
"end": "16:30"
}
]
}
]
}
]
},
"created_at": "2026-09-17T10:04:11.482913Z",
"updated_at": "2026-09-17T10:04:11.482931Z"
},
"matched_on": [
"password",
"provider_id",
"username"
],
"tier": "client"
}{
"error": {
"code": "reserved_credential",
"message": "Username 'goodlogin' is reserved for Truv's documented sandbox logins."
}
}{
"error": {
"code": "authentication_failed",
"message": "No such token"
}
}{
"error": {
"code": "env_not_sandbox",
"message": "Sandbox configuration requires a sandbox key; this key is 'prod'."
}
}{
"error": {
"code": "not_found",
"message": "Requested object not found."
}
}Resolve a sandbox scenario
A dry run: send the input a user would supply and get back the scenario that would answer it, along with the fields it constrained and the tier it came from.
If multiple scenarios match, Truv selects the scenario with more matching fields, excluding is_refresh. If there is still a tie, a scenario that sets is_refresh wins, and then the oldest scenario. A 404 means none of your scenarios matches the input, and the task would fall through to Truv’s built-in test data.
curl --request POST \
--url https://prod.truv.com/v1/sandbox/scenarios/resolve/ \
--header 'Content-Type: application/json' \
--header 'X-Access-Client-Id: <api-key>' \
--header 'X-Access-Secret: <api-key>' \
--data '
{
"data_source": "payroll",
"product": "income",
"username": "acme.salaried",
"password": "s4ndb0x",
"provider_id": "adp"
}
'import requests
url = "https://prod.truv.com/v1/sandbox/scenarios/resolve/"
payload = {
"data_source": "payroll",
"product": "income",
"username": "acme.salaried",
"password": "s4ndb0x",
"provider_id": "adp"
}
headers = {
"X-Access-Client-Id": "<api-key>",
"X-Access-Secret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Access-Client-Id': '<api-key>',
'X-Access-Secret': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
data_source: 'payroll',
product: 'income',
username: 'acme.salaried',
password: 's4ndb0x',
provider_id: 'adp'
})
};
fetch('https://prod.truv.com/v1/sandbox/scenarios/resolve/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://prod.truv.com/v1/sandbox/scenarios/resolve/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'data_source' => 'payroll',
'product' => 'income',
'username' => 'acme.salaried',
'password' => 's4ndb0x',
'provider_id' => 'adp'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Access-Client-Id: <api-key>",
"X-Access-Secret: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://prod.truv.com/v1/sandbox/scenarios/resolve/"
payload := strings.NewReader("{\n \"data_source\": \"payroll\",\n \"product\": \"income\",\n \"username\": \"acme.salaried\",\n \"password\": \"s4ndb0x\",\n \"provider_id\": \"adp\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Access-Client-Id", "<api-key>")
req.Header.Add("X-Access-Secret", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://prod.truv.com/v1/sandbox/scenarios/resolve/")
.header("X-Access-Client-Id", "<api-key>")
.header("X-Access-Secret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"data_source\": \"payroll\",\n \"product\": \"income\",\n \"username\": \"acme.salaried\",\n \"password\": \"s4ndb0x\",\n \"provider_id\": \"adp\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://prod.truv.com/v1/sandbox/scenarios/resolve/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Access-Client-Id"] = '<api-key>'
request["X-Access-Secret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data_source\": \"payroll\",\n \"product\": \"income\",\n \"username\": \"acme.salaried\",\n \"password\": \"s4ndb0x\",\n \"provider_id\": \"adp\"\n}"
response = http.request(request)
puts response.read_body{
"scenario": {
"id": "7b9f0c2a4d6e4f1b8c3a5d7e9f0b1c2d",
"data_source": "payroll",
"label": "Acme - salaried, biweekly",
"description": "Full-time salaried user with two paystubs",
"products": [
"income"
],
"enabled": true,
"username": "acme.salaried",
"password": "s4ndb0x",
"provider_id": null,
"company_domain": null,
"is_refresh": null,
"filenames": [],
"ssn_last4": null,
"date_of_birth": null,
"phone": null,
"behavior_status": "done",
"behavior_error_message": "",
"behavior_duration_seconds": 0,
"behavior_mfa": {
"challenges": [
{
"type": "TEXT",
"label": "Enter the code we sent to your phone",
"answer": "12345"
},
{
"type": "OPTIONS",
"label": "Where should we send your code?",
"options": [
{
"value": "sms",
"label": "Text message"
},
{
"value": "email",
"label": "Email"
}
],
"answers": [
"sms",
"email"
]
}
]
},
"payload": {
"flags": {
"is_fraudulent": false,
"is_suspicious": true
},
"employments": [
{
"profile": {
"full_name": "Jane Roe",
"first_name": "Jane",
"last_name": "Roe",
"ssn": "991-91-9991",
"date_of_birth": "1988-04-12",
"email": "jane.roe@acme.com",
"phone": "4155554193",
"home_address": {
"street": "1 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US"
}
},
"company": {
"name": "Acme Inc.",
"ein": "123456789",
"domain": "acme.com",
"phone": "4155554193",
"address": {
"street": "1 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US"
}
},
"job_title": "Staff Engineer",
"job_type": "F",
"start_date": "2022-03-01",
"end_date": null,
"original_hire_date": "2019-07-08",
"is_active": true,
"manager_name": "Jenny McDouglas",
"income": "124800.00",
"income_unit": "YEARLY",
"income_currency": "USD",
"pay_rate": "60.00",
"pay_frequency": "BW",
"bank_accounts": [
{
"bank_name": "Acme CU",
"account_name": "Everyday",
"account_number": "1234567890",
"routing_number": "111000025",
"account_type": "C",
"deposit_type": "A",
"deposit_value": "500.00"
}
],
"paystubs": [
{
"pay_date": "2026-08-14",
"period_start": "2026-08-01",
"period_end": "2026-08-14",
"currency": "USD",
"gross_pay": "4800.00",
"net_pay": "3312.00",
"gross_pay_ytd": "76800.00",
"net_pay_ytd": "52992.00",
"hours": "80.00",
"basis_of_pay": "S",
"earnings": [
{
"name": "Regular",
"amount": "4800.00",
"rate": "60.00",
"units": "80.00"
}
],
"earnings_ytd": [
{
"name": "Regular",
"amount": "76800.00",
"rate": "60.00",
"units": "1280.00"
}
],
"deductions": {
"Federal Tax": "1056.00",
"Social Security": "297.60",
"Medicare": "69.60",
"State Tax": "64.80"
},
"deductions_ytd": {
"Federal Tax": "16896.00",
"Social Security": "4761.60",
"Medicare": "1113.60",
"State Tax": "1036.80"
},
"document_id": "a41d7e9c05b24f6b8e3c1d5a7f92b604"
}
],
"w2s": [
{
"year": "2025",
"document_id": "3f1c8a90b7d2416e9a5c0e4f2b8d6a17",
"fields": {
"wages": "124800.00"
}
}
],
"shifts": [
{
"start_date": "2026-08-18",
"end_date": "2026-08-18",
"timezone": "America/Chicago",
"time_entries": [
{
"entry_date": "2026-08-18",
"start": "08:00",
"end": "16:30"
}
]
}
]
}
]
},
"created_at": "2026-09-17T10:04:11.482913Z",
"updated_at": "2026-09-17T10:04:11.482931Z"
},
"matched_on": [
"password",
"provider_id",
"username"
],
"tier": "client"
}{
"error": {
"code": "reserved_credential",
"message": "Username 'goodlogin' is reserved for Truv's documented sandbox logins."
}
}{
"error": {
"code": "authentication_failed",
"message": "No such token"
}
}{
"error": {
"code": "env_not_sandbox",
"message": "Sandbox configuration requires a sandbox key; this key is 'prod'."
}
}{
"error": {
"code": "not_found",
"message": "Requested object not found."
}
}Authorizations
Client ID
Client Access Key
Body
The input a user would supply, to be resolved against your scenarios.
Data source to resolve against. Inferred from the supplied fields when omitted, the same way a write infers it.
payroll, docs, financial_accounts, education, credit, insurance "payroll"
Product of the task being simulated. Scenarios pinned to other products are skipped.
income, employment, deposit_switch, pll, insurance, transactions, assets, admin, credit, education "income"
The login the user would type.
255"acme.salaried"
The password the user would type.
255"s4ndb0x"
Provider the connection would go through.
64"adp"
Company the connection would be made for.
128"acme.com"
Whether the task would be a data refresh.
false
Filenames the user would upload. Document upload only.
255[]
Last four SSN digits. Education and credit only.
4Date of birth. Education only.
Phone number, normalized to E.164 before matching. Credit only.
32Response
The scenario that would answer the supplied input, and why it won.
A client-authored sandbox case - what it matches on, how it behaves, and the data it returns.
Show child attributes
Show child attributes
Matcher fields the winning scenario actually constrained.
["password", "provider_id", "username"]
Where the winning scenario came from.
client "client"
Was this page helpful?