Standard Disbursements Flow
API Reference — Standard Disbursements Flow
The Standard Disbursements Flow stores the recipient and their debit card with Green Dot, so you can
pay them again without re-collecting their details. You create a recipient profile, link the card
they want to be paid to, and then send transfers to that card for as long as the relationship
lasts.
Every call in this flow returns its result in the response, and no webhooks are generated.
The flow
All paths are relative to {baseUrl}/programs/{programCode}.
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Arial, Helvetica, sans-serif", "fontSize": "15px", "actorBkg": "#00A550", "actorBorder": "#00A550", "actorTextColor": "#FFFFFF", "actorLineColor": "#B0B0B0", "signalColor": "#404040", "signalTextColor": "#404040", "noteBkgColor": "#F5F5F5", "noteBorderColor": "#C8C8C8", "noteTextColor": "#404040", "labelBoxBkgColor": "#FFFFFF", "labelBoxBorderColor": "#9E9E9E", "labelTextColor": "#404040", "loopTextColor": "#404040"}, "sequence": {"mirrorActors": false, "messageAlign": "center"}}}%%
sequenceDiagram
participant P as Your platform
participant GD as Green Dot API
Note over P,GD: Before you begin — program setup and account funding (no API call)
rect rgba(0, 165, 80, 0.08)
Note over P,GD: Once per recipient
P->>GD: Step 1 · POST /externalAccounts/customers
GD-->>P: 201 Created · customerToken
P->>GD: Step 2 · POST /externalAccounts/customers/{customerToken}/cards
GD-->>P: 201 Created · link.linkId
end
rect rgba(30, 110, 200, 0.08)
Note over P,GD: Every payout
P->>GD: Step 3 · POST /transfers (target = card link ID)
alt Processed
GD-->>P: 201 Created · transferStatus
else Downstream timeout
GD-->>P: 202 Accepted · Pending, Green Dot retries for up to 24 hours
end
end
Steps 1 and 2 run once per recipient and card. Step 3 runs once per payout. Each step hands the
next one the identifier it needs:
| Step | Call | Returns | Used in |
|---|---|---|---|
| 1 | Create a recipient profile | customerToken | Step 2 path parameter |
| 2 | Link the recipient's debit card | link.linkId (card link ID) | Step 3 targetTransferEndpoint.identifier |
| 3 | Send the disbursement | transfer.transferStatus | Your payout records |
Steps 1 and 2 run once per recipient and card. Step 3 runs once per payout. Each step hands the
next one the identifier it needs:
| Step | Returns | Used in |
|---|---|---|
| 1 — Create recipient profile | customerToken | Step 2 path parameter |
| 2 — Link the card | link.linkId (the card link ID) | Step 3 targetTransferEndpoint.identifier |
| 3 — Send the disbursement | transfer.transferStatus | Your records |
Before you start, make sure you have your program code, disbursement account identifier, access
token, and program public certificate. See Before You Begin.
A note on vocabulary. The API calls the person you are paying a customer, and the object
that represents them a customer profile. Throughout these guides they are the same thing as
your recipient.
Step 1 — Create a recipient profile
Creates the record that represents the person you are paying. The recipient's details go through
Green Dot's verification process, including OFAC screening, before they can be paid.
When to call it: Once per recipient, the first time you need to pay them. The profile persists;
do not create it again for the same person.
Syntax
POST {baseUrl}/programs/{programCode}/externalAccounts/customers
Request
POST {baseUrl}/programs/{programCode}/externalAccounts/customers HTTP/1.1
Authorization: Bearer {accessToken}
X-GD-RequestId: a0b1c2d3-e4f5-6789-abcd-ef0123456789
X-GD-CustomerType: external
Content-Type: application/json
Accept: application/json
{
"salt": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
"customerToken": "",
"encryptedData": {
"version": "EC_v1",
"ephemeralPublicKey": "BFz9k2Qc1XvR7mNp3JhTgWq8sYd4LbCe6AoUi0PxZrK=",
"publicKeyHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"data": "eyJmaXJzdE5hbWUiOiJBbGV4IiwibGFzdE5hbWUiOiJKb2huc29uIn0="
}
}Encrypted payload (before encryption)
{
"firstName": "Alex",
"lastName": "Johnson",
"middleName": "",
"email": "[email protected]",
"phoneNumber": "5550123456",
"dateOfBirth": "1988-04-12",
"address": {
"addressLine1": "123 Main Street",
"addressLine2": "",
"city": "Springfield",
"state": "IL",
"zipcode": "62701"
}
}The required set of identity fields varies by program and use case. Your program onboarding
documentation specifies exactly what your program requires.
Request parameters
| Parameter | Required | Type | Pattern | Description |
|---|---|---|---|---|
programCode | Yes | String | Path parameter | The program code assigned to you at onboarding. |
X-GD-RequestId | Yes | UUID | GUID | Passed as an HTTP header. Unique per request. |
X-GD-CustomerType | No | String | Defaults to external | Passed as an HTTP header. Leave at the default. |
salt | Yes | UUID | GUID | Must match the X-GD-RequestId header value. Used for idempotency and encryption. |
customerToken | Conditional | String | Max 50 | Leave empty to have Green Dot generate a token. Send your own unique value if your program is configured to supply one — some programs require it. |
encryptedData | Yes | Object | — | The recipient's identity details, encrypted with your program's public certificate. |
Response
HTTP/1.1 201 Created
Content-Type: application/json
{
"customerToken": "b1c2d3e4-f5a6-7890-bcde-f01234567890",
"status": "Pending",
"responseDetails": [
{
"code": 0,
"subCode": 0,
"description": "Success"
}
]
}Response parameters
| Parameter | Type | Description |
|---|---|---|
customerToken | String | The identifier for this recipient profile. Store it. It is the path parameter for Step 2 and for retrieving this recipient's transfers. |
status | String | The profile's status. A new profile starts as Pending. |
responseDetails | Array | Status code, sub-code, and description. code: 0 means success. |
Response codes
| Code | Description | Explanation |
|---|---|---|
| 201 | Created | The recipient profile was created. |
| 400 | Invalid or missing parameters | A required field is missing or malformed. Check the responseDetails sub-code. |
| 401 | Unauthorized | Token missing, expired, or out of scope. Request a new token and retry. |
| 403 | Forbidden | Your program is not authorized for this operation. |
| 500 | Internal Server Error | Retry the request. See the note on duplicate profiles below. |
| 503 | Service Unavailable | Retry the request. See the note on duplicate profiles below. |
Duplicate profiles. The duplicate check is based on
customerToken. If you supply a token that
already exists, the request is rejected with a "record already exists" error rather than creating
a second profile. If you leavecustomerTokenempty, Green Dot generates a new token on every
request, so a retry after a timeout can create a second profile. Supply your own tokens, derived
from your system of record, where your program allows it.
Step 2 — Link the recipient's debit card
Links the debit card the recipient wants to be paid to, and returns the card link ID you send
payouts to. Green Dot checks that the card's BIN is eligible for fast-funds push payments through
the card networks.
When to call it: Once per card, after the recipient profile exists. A recipient can have more
than one card linked. Call it again only to link a different card.
Syntax
POST {baseUrl}/programs/{programCode}/externalAccounts/customers/{customerToken}/cards
Request
POST {baseUrl}/programs/{programCode}/externalAccounts/customers/b1c2d3e4-f5a6-7890-bcde-f01234567890/cards HTTP/1.1
Authorization: Bearer {accessToken}
X-GD-RequestId: c2d3e4f5-a6b7-8901-cdef-012345678901
X-GD-CustomerType: external
Content-Type: application/json
Accept: application/json
{
"salt": "c2d3e4f5-a6b7-8901-cdef-012345678901",
"encryptedData": {
"version": "EC_v1",
"ephemeralPublicKey": "BFz9k2Qc1XvR7mNp3JhTgWq8sYd4LbCe6AoUi0PxZrK=",
"publicKeyHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"data": "eyJhY2NvdW50TnVtYmVyIjoiNDExMTExMTExMTExMTIzNCJ9"
}
}Encrypted payload (before encryption)
{
"firstName": "Alex",
"lastName": "Johnson",
"middleName": "",
"nickName": "Payout card",
"accountNumber": "4111111111111234",
"expiryMonth": "12",
"expiryYear": "2029",
"address": {
"addressLine1": "123 Main Street",
"addressLine2": "",
"city": "Springfield",
"state": "IL",
"zipCode": "62701"
}
}Card number, expiration, cardholder name, and billing address all travel inside the encrypted
payload. Nothing card-related is sent in plain text.
Request parameters
| Parameter | Required | Type | Pattern | Description |
|---|---|---|---|---|
programCode | Yes | String | Path parameter | The program code assigned to you at onboarding. |
customerToken | Yes | String | Path parameter | The customerToken returned in Step 1. |
X-GD-RequestId | Yes | UUID | GUID | Passed as an HTTP header. Unique per request. |
X-GD-CustomerType | No | String | Defaults to external | Passed as an HTTP header. Leave at the default. |
salt | Yes | UUID | GUID | Must match the X-GD-RequestId header value. |
encryptedData | Yes | Object | — | The card and cardholder details below, encrypted with your program's public certificate. |
Encrypted payload fields
| Field | Required | Type | Description |
|---|---|---|---|
accountNumber | Yes | String | The debit card number. |
expiryMonth | Yes | String | Expiration month, MM. |
expiryYear | Yes | String | Expiration year, YYYY. |
firstName | Yes | String | Cardholder's first name, as it appears on the account. |
lastName | Yes | String | Cardholder's last name, as it appears on the account. |
middleName | No | String | Cardholder's middle name. |
nickName | No | String | A label for the card, useful when a recipient has more than one. |
address | Yes | Object | Cardholder billing address: addressLine1, addressLine2, city, state (2 characters), zipCode (5 digits). |
Response
HTTP/1.1 201 Created
Content-Type: application/json
{
"link": {
"linkId": "d3e4f5a6-b7c8-9012-def0-123456789012"
},
"responseDetails": [
{
"code": 0,
"subCode": 0,
"description": "Success"
}
]
}Response parameters
| Parameter | Type | Description |
|---|---|---|
link.linkId | UUID | The card link ID. Store it against your record of the recipient. It is the destination identifier for every payout to this card. |
responseDetails | Array | Status code, sub-code, and description. code: 0 means success. |
Response codes
| Code | Description | Explanation |
|---|---|---|
| 201 | Created | The card was linked. |
| 400 | Invalid or missing parameters | A required field is missing or malformed. Check the sub-code. |
| 401 | Unauthorized | Token missing, expired, or out of scope. Request a new token and retry. |
| 403 | Forbidden | Your program is not authorized for this operation. |
| 404 | Not Found | The recipient profile for this customerToken was not found. |
| 500 | Internal Server Error | Retry the request. Linking the same card again returns the existing link. |
| 503 | Service Unavailable | Retry the request. Linking the same card again returns the existing link. |
How card links behave
- Linking is idempotent. Linking a card that is already linked to the same recipient returns
the existing card link instead of creating a new one. - Link status changes after the first payout. A new card link starts as
InActiveand becomes
Activeafter the first successful transfer to it. AnInActivelink can still receive a
payout. - Expiration is validated when the card is linked.
Step 3 — Send the disbursement
Moves money from your disbursement business account to the recipient's linked card.
When to call it: Every time you pay the recipient. In steady state, this is the only call you
make.
Syntax
POST {baseUrl}/programs/{programCode}/transfers
Request
POST {baseUrl}/programs/{programCode}/transfers HTTP/1.1
Authorization: Bearer {accessToken}
X-GD-RequestId: e4f5a6b7-c8d9-0123-ef01-234567890123
Content-Type: application/json
Accept: application/json
{
"transferIdentifier": "f5a6b7c8-d9e0-1234-f012-345678901234",
"transferType": "DisbursementExternal",
"transferAuthorizationType": "execute",
"initiator": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"partnerReferenceData": "PAYRUN-20260115-0042",
"transferRoute": {
"transactionAmount": 100.00,
"transactionDescription": "Weekly payout",
"sourceTransferEndpoint": {
"transferEndPointType": "programFundingSource",
"identifier": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"currency": "USD"
},
"targetTransferEndpoint": {
"transferEndPointType": "card",
"identifier": "d3e4f5a6-b7c8-9012-def0-123456789012",
"currency": "USD"
}
}
}The source is your disbursement business account. The target is the card link ID from Step 2.
Request parameters
| Parameter | Required | Type | Pattern | Description |
|---|---|---|---|---|
programCode | Yes | String | Path parameter | The program code assigned to you at onboarding. |
X-GD-RequestId | Yes | UUID | GUID | Passed as an HTTP header. Unique per request. |
transferIdentifier | Yes | UUID | GUID | Your unique identifier for this payout. Reuse it on retries; never reuse it across payouts. |
transferType | Yes | String | DisbursementExternal | Use DisbursementExternal for a disbursement. |
transferAuthorizationType | Yes | String | execute | Must be execute. Disbursements are single-phase; there is no separate authorize step. |
initiator | No | String | 36 characters | Your disbursement account identifier. Optional; if supplied, it must match the source. |
partnerReferenceData | No | String | See note below | Your own reference for this payout. Can be included in settlement reporting on request, which makes reconciliation against your records straightforward. |
transferRoute | Yes | Object | — | Amount, source, and destination for the payout. |
transferRoute.transactionAmount | Yes | Decimal | e.g. 100.00 | The amount to send. |
transferRoute.transactionDescription | No | String | Max 250 | Description carried with the transaction. |
sourceTransferEndpoint.transferEndPointType | Yes | String | programFundingSource | Use programFundingSource. |
sourceTransferEndpoint.identifier | Yes | String | 36 characters | Your disbursement account identifier, issued at onboarding. |
sourceTransferEndpoint.currency | No | String | 3-character ISO | Defaults to USD. Only USD is supported. |
targetTransferEndpoint.transferEndPointType | Yes | String | card | Use card for a payout to a linked debit card. |
targetTransferEndpoint.identifier | Yes | UUID | GUID | The card link ID (link.linkId) returned in Step 2. |
targetTransferEndpoint.currency | No | String | 3-character ISO | Defaults to USD. Only USD is supported. |
fraudData | No | Object | Key/value pairs | Additional fraud signals for this payout. |
deviceDetails | No | Object | — | Device type, device token, and IP address of the device that triggered the payout. Recommended when the recipient initiated the request. |
partnerReferenceData accepts printable ASCII characters except comma (,), semicolon (;),
backtick (`), and pipe (|).
Response
HTTP/1.1 201 Created
Content-Type: application/json
{
"transfer": {
"transferIdentifier": "f5a6b7c8-d9e0-1234-f012-345678901234",
"transferStatus": "Completed"
},
"responseDetails": [
{
"code": 0,
"subCode": 0,
"description": "Success"
}
]
}Response parameters
| Parameter | Type | Description |
|---|---|---|
transfer.transferIdentifier | String | The identifier for this payout — the same value you sent. |
transfer.transferStatus | String | The status of the payout. See Transfer Status & Webhooks. |
responseDetails | Array | Status code, sub-code, and description. code: 0 means success. |
Error response
HTTP/1.1 400 Bad Request
Content-Type: application/json
[
{
"code": 4200,
"subCode": 940,
"description": "Invalid Transaction Amount"
}
]Response codes
| Code | Description | Explanation |
|---|---|---|
| 201 | Created | The payout was processed. Read transferStatus for the outcome. |
| 202 | Accepted | A downstream timeout occurred. The payout is Pending while Green Dot retries automatically. See When a payout is pending. |
| 400 | Bad Request | Validation failed. The body identifies the field. |
| 401 | Unauthorized | Token missing, expired, or out of scope. Request a new token and retry. |
| 403 | Forbidden | Your program is not authorized for this transfer type. |
| 404 | Not Found | The program, disbursement account, or card link could not be found. |
| 500 | Internal Server Error | Retry with the same transferIdentifier. |
| 503 | Service Unavailable | Retry with the same transferIdentifier. |
See [Response Codes](./08-response-codes.md) for the full sub-code reference.
Tracking payouts
Because this flow is synchronous, the transfer response tells you the outcome directly in most
cases. For reconciliation, support questions, and any payout that returned Pending, retrieve the
transfer by customer token and transfer identifier.
See the External Accounts API reference for Retrieve a transfer by customer token and
transfer ID and Search transfers by date range for a customer profile.
Managing recipients and cards
The External Accounts API group also lets you maintain what you have stored:
| To do this | Use |
|---|---|
| Update a recipient's details | Update an existing customer profile |
| Retrieve a recipient's details | Retrieve a customer profile |
| Update a linked card | Update a card link for a customer profile |
| Remove a card the recipient no longer wants to use | Delete a card link from a customer profile |
A deleted card link cannot be used for new payouts.
Paying the same recipient again
Once Steps 1 and 2 are done, every future payout is Step 3 on its own. Store customerToken and
the card link ID against your own record of the person, and reuse them. Do not recreate the profile
or relink the card.
Paying many recipients
Payouts are initiated one at a time. To run a payout batch, iterate your list and call Step 3 once
per recipient, generating a fresh transferIdentifier for each. Track each payout independently —
one failure in a run does not affect the others, and each needs its own resolution.
Before a large run, confirm your disbursement account holds enough funds to cover the whole batch.
Updated about 23 hours ago
