Gig Payouts
Debit card payouts for gig workers
Overview
Gig platforms and earned wage access providers often pay workers in more than one way:
- Direct deposit: A scheduled transfer of available earnings to the user's bank account, typically weekly.
- Debit card payout: An on-demand payment to the bank or prepaid account behind an eligible debit card, for a fee. These payouts travel over push-to-card services such as Visa Direct or Mastercard Send, not ACH, and often arrive within seconds or minutes. Real-Time Payments (RTP) and FedNow can also move money instantly, but they use bank account details and are not debit card payouts.
- Platform-issued account or card: A financial account or debit card offered by the platform itself, often with instant or low-cost access to earnings.
- PayPal and Venmo: Digital wallet payout options that some platforms offer alongside bank and card payouts.
With Gig Payouts, one Transact session can update both the direct deposit and the debit card. When your user switches their direct deposit to a supported platform, Atomic also puts your debit card on file as the platform's fast-payout card, so the user's earnings reach you however they choose to cash out.
Integration changes
- Add
deferredPaymentMethodStrategyto your UserLink Transact SDK configuration. - Optionally, add partial debit card details to your access token request, so users see the card's name and last four in Transact.
- Handle the new debit card switch request in
onDataRequest.
This is purely additive: however you pass deposit accounts and identity today stays exactly as it is. Contact your Atomic representative to have the feature enabled for your account.
Platform support
| Platform | Scheduled payout (bank) | Fast payout (debit card) | Platform-issued account | Support |
|---|---|---|---|---|
| DoorDash | Weekly pay | Fast Pay (daily) | DoorDash Crimson | Available |
| Uber | Weekly payout | Instant Pay / Cash Out | Uber Pro Card | Coming soon |
| Lyft | Weekly payout | Express Pay | Lyft Direct | Coming soon |
| DailyPay | Next business day | Instant transfer | DailyPay Visa® Prepaid Card | Planned |
| Payactiv | Varies by employer | Real-time transfer | Payactiv Visa® Card | Planned |
Scheduled payouts land in the user's bank account automatically. Fast payouts are on-demand transfers to a debit card, and the platform always charges a fee for them.
DoorDash is available today. Uber and Lyft are coming soon. DailyPay and Payactiv are earned wage access platforms Atomic plans to support next.
Atomic updates the bank payout and the debit card payout. It never changes the platform's own issued account.
DoorDash specifics
DoorDash may lock Fast Pay for 7 days after the card changes. Transact tells the user this before they opt in. Plan your user messaging around this if fast payout is central to your value proposition.
How it works
1
The user picks their account and selects a supported platform, exactly as they would for any direct deposit switch.
2
The user opts in: Before sign-in, Transact shows the direct deposit account and a toggle, on by default, to also put your debit card on file for fast payouts. It shows your card's last four if you provided one. On DoorDash the toggle reads "Set up Fast Pay." Turning it off updates the direct deposit only.
3
The user signs in once: A single login to the platform covers both updates.
4
Atomic asks you for the card: Transact sends a debit card switch request through
onDataRequest, withproduct: "switch", and you supply the debit card and the cardholder's billing identity.5
Atomic updates the platform: The debit card goes on file for fast payouts and the bank account is updated for scheduled payouts. If the platform asks the user to verify their identity, Transact handles that step.
6
Atomic reports the results: You receive one webhook for the
switchtask that adds the debit card and one for thedeposittask that updates direct deposit. If one update fails, the other is not rolled back.
Step 1: Configure the session
Choose a deferred payment method strategy. It tells Atomic you will supply the debit card during the session rather than up front. Pick the strategy that matches where your card data lives.
| Strategy | You respond by | Card data travels |
|---|---|---|
sdk | Returning the card from onDataRequest. Transact takes it from there. | From the user's device straight to the platform. Use this when the card is available in your app. |
api | Sending the card from your backend to Atomic's PCI endpoint at pci.atomicfi.com, which tokenizes it. See Using the API strategy below. | From your backend to Atomic's PCI environment. Use this when the card already lives server-side. |
Optional: add the debit card to the access token
When you create the user's access token with POST /access-token, you can list their debit card with partial details alongside the deposit account you already send. Transact shows its name and last four on the opt-in screen, so the user knows which card goes on file, and echoes them back in the debit card switch request.
{
// your existing deposit access-token creation payload
"identifier": "your-user-id",
"accounts": [
{
"type": "checking",
"title": "Premier Plus Checking",
"routingNumber": "124003116",
"accountNumber": "2157834891"
}
],
// optionally, you can add 1 card
"cards": [
{
"lastFour": "0008",
"title": "Everyday Debit",
"cardType": "debit",
"externalId": "card_8f2c"
}
]
} Debit cards only, with cardType set to exactly "debit", in lowercase. Other values such as "Debit" are accepted when the token is created but Transact ignores the card. Send just lastFour and title, never the card number, so the access token stays out of PCI scope. externalId is optional: your own ID for the card, so you know which card to send. Only the first debit card is used, and if you list one, include it every time you create an access token for the user.
Add the strategy to your SDK configuration
import AtomicTransact
let config = AtomicConfig(
publicToken: "PUBLIC_TOKEN",
scope: .userLink,
tasks: [.init(operation: .deposit)],
// Add this line. .sdk keeps card data on the user's device.
deferredPaymentMethodStrategy: .sdk
) Requires iOS SDK 3.28.0 or later, the first version with CardData.cardType.
Step 2: Handle the debit card switch request
When the user opts in, Transact sends a debit card switch request through onDataRequest. The product field identifies it as switch. Check that field first and handle only switch here. Any other data request your integration already receives keeps working exactly as it does today.
{
"product": "switch",
"fields": [
"identity",
"card"
],
"userId": "6a9...",
"identifier": "your-user-id",
"taskId": "6a9...",
"taskWorkflowId": "6a9...",
"company": {
"_id": "5df8...",
"name": "DoorDash"
},
"properties": {
"lastFour": "0008",
"title": "Everyday Debit",
"externalId": "card_8f2c"
}
}fields always includes identity. It includes card only when Atomic still needs the card number, so check fields.includes("card") rather than matching the whole array. properties echoes the card you added to the access token in Step 1. If you did not add one, it is empty. taskId is the ID of the deposit task in the same session, not the switch task.
On iOS, userId, identifier, fields, and taskId are typed properties on TransactDataRequest. Everything else, including product and taskWorkflowId, is read from the data dictionary.
Send the card and the billing identity together
A debit card switch request asks for both. Its identity is the cardholder's name and billing address, which the platform verifies against the card. It is not the same identity data used for direct deposit. How you answer depends on your strategy: under sdk you return the values from your handler and Atomic submits them for you, and under api your backend sends them to pci.atomicfi.com.
Examples
Both handle the same debit card switch request; they differ only in where the card goes.
deferredPaymentMethodStrategy: .sdk: return the card and billing identity. Transact submits them for you.
Atomic.presentTransact(
from: self,
config: config,
onDataRequest: { request in
if request.data["product"] as? String == "switch" {
// Debit card switch request. properties echoes the Step 1 card
// (lastFour "0008", externalId "card_8f2c"), so send that card.
// Use a sandbox test card (see Testing) when testing in sandbox.
// Replace this with your actual data collection logic in production.
let card = TransactDataResponse.CardData(
number: "5100000000000008",
expiry: "03/29",
cvv: "111",
cardType: .debit
)
let identity = TransactDataResponse.Identity(
firstName: "Jane",
lastName: "Dasher",
postalCode: "12345",
address: "123 Main St",
city: "New York",
state: "NY"
)
guard identity.isValid() else { return nil }
return TransactDataResponse(card: card, identity: identity)
} else {
// Any other existing deposit data request handling code you have
return nil
}
},
onTaskStatusUpdate: { task in
print("Task \(task.taskId) status: \(task.status)")
}
)deferredPaymentMethodStrategy: .api: send the card from your backend to Atomic's PCI endpoint, which tokenizes it and attaches it to the task.
Atomic.presentTransact(
from: self,
config: config, // deferredPaymentMethodStrategy: .api
onDataRequest: { request in
if request.data["product"] as? String == "switch" {
// Debit card switch request. Your backend sends the card matching
// properties.externalId ("card_8f2c") from the Step 1 access token.
// Your backend PUTs the card and billing identity with this
// taskWorkflowId to https://pci.atomicfi.com/user in production,
// or https://sandbox-pci.atomicfi.com/user in sandbox.
await yourBackend.sendCardToAtomicPci(
identifier: request.identifier,
taskWorkflowId: request.data["taskWorkflowId"] as? String
)
// Nothing is returned to Transact; your backend submits the card.
return nil
} else {
// Any other existing deposit data request handling code you have
return nil
}
}
)pci.atomicfi.com in production and sandbox-pci.atomicfi.com in sandbox. When testing in sandbox, only use the test card numbers listed in Testing. Card and identity fields
| Object | Required | Optional |
|---|---|---|
card | number, expiry (MM/YY), cvv | cardType: set .debit for a known debit card. This declares the card type; it does not validate whether the card is debit or credit. |
identity | firstName, lastName, address, city, state, postalCode | address2, phone, email |
Apart from the card number, every field is optional in the SDK types, but the debit card switch needs the ones listed as required. Use the two-letter state abbreviation: identity.validate() flags any other length, as well as a postal code outside 5 to 10 characters, a first or last name over 50 characters, or an address or city over 100. The billing identity should match what the card issuer has on file.
Using the API strategy
With api, Transact never collects the card itself. Your backend sends it when the debit card switch request arrives. Four things to get right:
| Do this | Or else |
|---|---|
Send the card shown to the user, matched by externalId or the last four on the debit card switch request. If you did not list a card, send the user's debit card. | A different card is not rejected. Whatever you send is the card that goes on file. |
Include the request's taskWorkflowId in your PUT /user to pci.atomicfi.com. | The card may not be attached to this session. With taskWorkflowId, the card is used for this session only and is not saved to the user's cards. Without it, Atomic matches the card to the user's cards by last four. |
Include the billing identity in the card's PUT /user, unless the user record already has it. | The request fails with a missing identity error. Identity you send this way is also saved to the user. |
| Send the requested card promptly. | Queued workflows become eligible for cancellation 5 minutes after workflow creation. Cancellation typically occurs after 5 to 10 minutes. A data request does not restart this timeout. |
From your backend, authenticate the PUT /user request with both x-api-key and x-api-secret. Keep identifier at the top level and nest taskWorkflowId, card, and identity under data:
{
"identifier": "your-user-id",
"data": {
"taskWorkflowId": "TASK_WORKFLOW_ID_FROM_ON_DATA_REQUEST",
"card": {
"number": "5100000000000008",
"expiry": "03/29",
"cvv": "111",
"cardType": "debit"
},
"identity": {
"firstName": "Jane",
"lastName": "Dasher",
"address": "123 Main St",
"city": "New York",
"state": "NY",
"postalCode": "12345"
}
}
}If you also defer the deposit account
If you already launch the user with a full deposit account in the access token, no change is needed here.
{
"identifier": "your-user-id",
"accounts": [
{
"type": "checking",
"title": "Premier Plus Checking",
"routingNumber": "124003116",
"accountNumber": "2157834891"
}
]
} If you pass only partial deposit account details in the access token and provide the full account just in time through your existing deposit data request, onDataRequest now fires twice in the same session: once for the deposit account and once for the debit card switch request. Make sure your handler checks product and fields before responding.
product | fields | Respond with |
|---|---|---|
deposit | Includes account | The full deposit account, as you do today. |
switch | Includes identity, and card when needed | The debit card and billing identity, through your chosen strategy. |
- Answer each request on its own. Keep the account out of the debit card switch response, and the card out of the deposit account response.
- Send the account and the card in separate calls. A single
PUT /userwith both is rejected. The deposit account always comes from your backend with your API key, even under thesdkstrategy, because the SDK response carries only the card and identity. - Order does not matter. Neither update starts until both pieces have arrived.
- Answer both promptly. If only one arrives, the workflow stays queued and neither update runs, including the direct deposit. The timeout starts at workflow creation, not when either data request arrives.
What the user sees
Both updates run behind one progress screen. The user doesn't see two flows or sign in twice.
The outcome screen reflects the debit card update, which finishes last. It does not report the direct deposit result separately, so use the deposit task's webhook to confirm the direct deposit before you message the user about it.
Task results and errors
The direct deposit and the debit card are separate tasks, and each sends its own webhook: a switch task for the debit card and a deposit task for direct deposit. The switch task's events are described in the PayLink webhooks reference. If one fails, the other is not rolled back. These are the fail reasons you may see on each.
| Scenario | Task | Fail reason |
|---|---|---|
| The card is a credit card, or a debit card the platform can't send fast payouts to | switch | payment-method-not-supported |
| The card could not be added for another reason | switch | payment-switch-unsuccessful |
| The platform rejected the bank account | deposit | account-unusable |
The user did not complete the platform's identity verification. The switch task does not run, so the card is not added either. | deposit | identity-verification-required |
If the platform already has a card with the same last four on file, the switch task completes without changing it.
How card data is handled
Both strategies share the same guarantee: the card number and CVV are entered into the gig platform from the user's own device, never from an Atomic server. They are scoped to the single task that needs them, are never written to your Atomic user record, and never appear in task logs or request history. The strategies differ only in how the card gets to the device.
sdkkeeps the card on the device. Your app returns it fromonDataRequest, and Atomic's servers never receive the card number or CVV.apitokenizes the card in Atomic's PCI environment. Your backend sends it topci.atomicfi.com, and only a token reaches the rest of Atomic.
sdk | api | |
|---|---|---|
| Where Atomic receives the card | Nowhere. It stays on the device. | pci.atomicfi.com only, then tokenized |
| What Atomic's services store | A stand-in token, expiry, last four, brand, and card type (no number or CVV) | A token, plus expiry and last four |
| Who sends the card to the platform | The user's device | The user's device |
| After the task finishes | Atomic attempts to clear card data from device memory | Atomic attempts to clear card data from device memory |