Worker Data via Paystream
Overview
Paystream is Salsa's data ingestion API that allows you to create and update worker records, including personal information, addresses, bank accounts, contracts, and more. This guide explains what worker data you can send through Paystream, what can be updated, and important limitations to be aware of.
For the end-to-end sync pattern (employer first, then workers, and which system owns each field), see Paystream.
Worker Data Categories
Worker data in Paystream is organized into the following categories:
| Category | What It Includes | Create | Update |
|---|---|---|---|
| Basic Information | First name, last name, middle name, display name | ✓ | ✓ |
| Personal Information | Email, birth date, SSN/SIN, phone number, business contractor info | ✓ | ✓ |
| Home Address | Full address details including street, city, state, zip, country | ✓ | ✓ |
| Bank Accounts | Account type, routing number, account number, payment distribution | ✓ | ⚠️ Limited |
| Contract | Classification (employee/contractor), hire date, overtime eligibility | ✓ | ❌ No |
| Termination | Termination date, category, and notes | ✓ | ✓ |
Creating vs Updating Workers
How Upsert Works
Paystream uses an upsert pattern - the same endpoint handles both creating new workers and updating existing ones. The system determines whether to create or update based on the presence of the Salsa worker ID:
- Include
id(Salsa's worker ID) → Updates the existing worker - Omit
id→ Creates a new worker
Use External IDs for Your ReferenceInclude an
externalIdfrom your system when creating workers. Worker external IDs must be unique across all workers, not just within one employer; if yours are only unique per employer, prefix them with the employer ID. This helps you maintain the mapping between your system and Salsa. Both IDs (workerIdfrom Salsa and yourexternalId) will be returned in the response.
Synchronous vs Asynchronous Processing
Paystream offers two approaches for ingesting worker data:
Synchronous (Recommended for most use cases)
Endpoint: POST /v1/paystream/workers
Scope: PAYSTREAM_WORKER_WRITE
Returns results immediately with all created/updated worker IDs:
{
"data": [
{
"workerId": "worker_xyz789",
"externalId": "your-system-id-123",
"workerContract": {
"id": "contract_abc456"
},
"bankAccounts": [
{
"id": "bankaccount_def789",
"externalId": "bank-external-123"
}
]
}
]
}Use when: You need immediate confirmation and IDs for further processing.
A request where some workers fail and others succeed still returns 201, with one entry per failed worker in errors. Always read errors, not only the status code.
Asynchronous ("Send and Forget")
Endpoint: POST /v1/paystream
Scope: PAYSTREAM_ASYNC_WRITE
Queues items for background processing and returns immediately. Add ?synchronous=true to process the items inline instead.
Use when: Processing large batches where you don't need immediate results.
Getting Results: Subscribe to webhook events to receive workerId and externalId when processing completes:
{
"eventType": "Worker.created",
"data": {
"workerId": "worker_xyz789",
"workerExternalId": "your-system-id-123",
"employerId": "employer_abc123",
"employerExternalId": "your-employer-ref"
}
}
Webhook TipWebhook events include both Salsa IDs and your external IDs, making it easy to create mappings in your database without additional API calls.
Example: Creating a Worker
{
"data": [
{
"type": "PaystreamWorkerUpsertInput",
"employerId": "employer_abc123",
"externalId": "your-system-worker-456",
"firstName": "Jane",
"lastName": "Doe",
"displayName": "Jane Doe",
"personalInfo": {
"email": "[email protected]",
"birthDate": "1990-05-15",
"individualIdentificationNumber": "123-45-6789",
"phoneNumber": {
"number": "(555) 123-4567",
"countryCode": "+1"
}
},
"homeAddress": {
"name": "Home",
"addressLine1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postalCode": "94105",
"countryISO": "USA"
},
"contract": {
"classification": "EMPLOYEE",
"effectiveDate": "2024-01-01",
"overtimeEligible": true,
"hireDate": "2024-01-01"
}
}
]
}Example: Updating a Worker
{
"data": [
{
"type": "PaystreamWorkerUpsertInput",
"id": "worker_xyz789",
"employerId": "employer_abc123",
"personalInfo": {
"email": "[email protected]",
"phoneNumber": {
"number": "(555) 987-6543",
"countryCode": "+1"
}
},
"homeAddress": {
"name": "Home",
"addressLine1": "456 Oak Avenue",
"city": "Oakland",
"state": "CA",
"postalCode": "94612",
"countryISO": "USA"
}
}
]
}What You Can Update
Fully Updatable
These fields can be freely updated at any time:
Basic Information
- First name, middle name, last name
- Display name
Tip: Updating NamesYou can update
firstNameandlastNameindependently. If you only sendfirstName, thelastNameremains unchanged (and vice versa).displayNameis not regenerated, so send it too if it should change.
Personal Information
- Email address
- Birth date
- SSN/SIN (individual identification number)
- Phone number
- Business contractor information (EIN/BN, business name)
Address
- All address fields (street, city, state, zip, country)
- Address is always replaced in full - partial updates are not supported
Termination
- Termination date, category, and employer notes
- Used to mark a worker as terminated
Limited Update Capability
Bank Accounts
Bank accounts have special update rules:
- Before first use: Can be updated freely
- After use in payroll: Become immutable
- Attempting to update a used account: Creates a new bank account instead of updating the existing one
Best Practice: Always create a new bank account when changes are needed rather than attempting to update an existing one.
Setting Default Payment Account:
{
"bankAccounts": [
{
"accountType": "CHECKING",
"routingNumber": "123456789",
"accountNumber": "987654321",
"displayName": "Primary Checking",
"setAsExclusivePayDistributionRule": true
}
]
}
Important: Exclusive Payment DistributionSetting
setAsExclusivePayDistributionRule: truereplaces ALL existing payment distribution rules and makes this bank account the sole recipient of funds. Only one bank account per request can have this flag set totrue.
Cannot Be Updated
Worker Contract
Worker contracts can only be created during initial worker creation. They cannot be updated via Paystream.
Contract fields (create-only):
- Classification (EMPLOYEE or CONTRACTOR)
- Effective date
- Overtime eligibility
- Hire date
- End date
Overtime eligibility drives overtime calculationA worker created with
overtimeEligible: false(or without a value) is never evaluated for overtime, and payroll runs do not flag it. Set it when you create the worker, or confirm the employer completes it before the first payroll run. See Who gets overtime.
Contract UpdatesTo update a worker's contract after creation, you must use the Worker Contract API (not Paystream). Attempting to update a contract via Paystream will return an error.
Why This Restriction?
Worker contracts are foundational to payroll calculations and tax compliance. Changes to classification, overtime eligibility, or contract dates require careful handling through the dedicated Advanced API to ensure proper tax calculations and compliance. Let us know if you want to own the contract data, and we will help you.
Special Data Types
Business Contractors
Workers classified as contractors can operate as a business entity:
{
"contract": {
"classification": "CONTRACTOR"
},
"personalInfo": {
"isBusinessContractor": true,
"businessIdentificationNumber": "12-3456789",
"businessName": "Jane Doe Consulting LLC",
"individualIdentificationNumber": null
}
}When isBusinessContractor: true:
- Use
businessIdentificationNumber(EIN/BN) instead ofindividualIdentificationNumber(SSN/SIN) - Provide
businessNamefor the business entity - Taxes will be filed under the business entity
Worker Termination
To terminate a worker, send termination details:
{
"termination": {
"terminationDate": "2024-12-31",
"category": "VOLUNTARY",
"employerNotes": "Resigned for new opportunity"
}
}Termination Categories:
VOLUNTARY- Worker resignedINVOLUNTARY- Worker was terminatedRETIREMENT- Worker retiredDECEASED- Worker deceasedOTHER- Other reason
Best Practices
1. Store Both Salsa Worker IDs and Your External IDs
When you create a worker, the response includes Salsa's workerId. You must store this ID in your system to perform updates later.
// Response from creation:
{
"workerId": "worker_xyz789",
"externalId": "your-system-id-123"
}Store both IDs:
workerId- Required for updates via PaystreamexternalId- Your reference for mapping between systems
2. Know Which Updates Are Partial
Updates are partial for names and personal information, but not for the address:
- Names:
firstName,middleName,lastNameanddisplayNameyou omit keep their current value.displayNameis not regenerated when you change a name, so send it too if it should change. - Personal information: fields you omit (
birthDate,email,phoneNumber, identification numbers) are left unchanged. Omitting a field never clears it. - Home address: send the complete address. It replaces the stored address in full.
{
"personalInfo": {
"email": "[email protected]"
}
}This changes only the email; the other personalInfo fields keep their values.
3. Handle Bank Accounts Immutability
Always create new bank accounts rather than attempting to update existing ones:
{
"bankAccounts": [
{
"externalId": "bank-account-v2",
"accountType": "CHECKING",
"routingNumber": "987654321",
"accountNumber": "123456789",
"setAsExclusivePayDistributionRule": true
}
]
}Use externalId to track different versions of bank accounts in your system.
Related Resources
- Paystream - Overview, endpoint choice, and syncing employer and worker data
- Paystream API Reference - Complete API reference with all available fields
- Webhook Events - Subscribe to worker creation and update events for async processing
Updated about 1 hour ago
