Single Call Disbursements Flow (Async)
API Reference — Single Call Disbursement Flow (Async)
The Single Call Disbursements Flow (Async) sends a payout in one request. Recipient details, card
details, and the amount travel together, and the final outcome is delivered asynchronously to your
webhook endpoint.
Nothing is stored for reuse: each payout carries everything it needs. That fits programs where
recipients are paid once or occasionally and there is no ongoing relationship to maintain.
The flow
%%{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
participant W as Your webhook endpoint
Note over P,W: Before you begin — program setup, account funding, and webhook registration (no API call)
rect rgba(0, 165, 80, 0.08)
Note over P,GD: Every payout
P->>GD: Step 1 · POST /enrollment/v1/api/flex/transfers/singlecommit
Note right of P: Recipient details, encrypted card data, and amount in one request
GD-->>P: 201 Created · transferStatus Pending
end
rect rgba(30, 110, 200, 0.08)
Note over GD,W: Asynchronous outcome
GD->>W: Step 2 · POST /events/SinglePhaseTransfer
Note right of GD: Completed, Failed, or Declined
W-->>GD: Acknowledge receipt
end
opt Webhook missed or delayed
P->>GD: GET /enrollment/v1/api/flex/externalAccounts/{externalAccountId}/transfers/{transferId}
GD-->>P: Current transferStatus
end
| Step | What happens | What you receive |
|---|---|---|
| 1 | You send the payout in a single request | 201 Created with transferStatus: Pending |
| 2 | Green Dot delivers the final outcome to your webhook endpoint | Completed, Failed, or Declined |
| Fallback | You look up the payout if a webhook is missed | The current transferStatus |
Before you start, make sure you have your disbursement account identifier, payee site identifier,
access token, program public certificate, and a webhook endpoint that is deployed and reachable.
See Before You Begin.
This flow requires a webhook endpoint. The
201response confirms the request was accepted,
not that the money arrived. The webhook is how you learn the final outcome, including declines.
Step 1 — Send the disbursement
Creates and executes a payout to a debit card in one call.
When to call it: Once per payout. There is no setup call and no prior state.
Syntax
POST {baseUrl}/enrollment/v1/api/flex/transfers/singlecommit
Environment base URLs are issued during onboarding.
Request
POST {baseUrl}/enrollment/v1/api/flex/transfers/singlecommit HTTP/1.1
x-Remapped-Authorization: Bearer {accessToken}
Request-ID: 1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809
Content-Type: application/json
{
"transferIdentifier": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
"transferType": "DisbursementExternal",
"initiator": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"transferdescription": "Settlement payment",
"transferRoute": {
"transactionAmount": 20.00,
"sourceTransferEndpoint": {
"transferEndPointType": "programFundingSource",
"identifier": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"currency": "USD"
},
"targetTransferEndpoint": {
"transferEndPointType": "singlePhaseFunding",
"identifier": "3c4d5e6f-7081-92a3-b4c5-d6e7f8091234",
"currency": "USD",
"encryptedCardData": {
"version": "EC_v1",
"ephemeralPublicKey": "BFz9k2Qc1XvR7mNp3JhTgWq8sYd4LbCe6AoUi0PxZrK=",
"publicKeyHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"data": "eyJjYXJkTnVtYmVyIjoiNDExMTExMTExMTExMTIzNCJ9"
},
"encryptedUserData": {
"version": "EC_v1",
"ephemeralPublicKey": "BFz9k2Qc1XvR7mNp3JhTgWq8sYd4LbCe6AoUi0PxZrK=",
"publicKeyHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"data": "eyJmaXJzdE5hbWUiOiJBbGV4IiwibGFzdE5hbWUiOiJKb2huc29uIn0="
}
}
}
}Request headers
| Header | Required | Type | Pattern | Description |
|---|---|---|---|---|
x-Remapped-Authorization | Yes | String | Bearer {accessToken} | Your OAuth 2.0 access token. This endpoint reads the token from this header rather than Authorization. |
Request-ID | Yes | UUID | GUID | Unique per request. |
Content-Type | Yes | String | application/json | — |
Request parameters
| Parameter | Required | Type | Pattern | Description |
|---|---|---|---|---|
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. |
initiator | No | String | 36 characters | The account the payout is initiated from. Optional in this flow; if supplied, it must be the source or the target. |
transferdescription | No | String | Max 100 | A description of the payout. Note the field name is all lowercase in this endpoint. |
transferRoute.transactionAmount | Yes | Decimal | e.g. 20.00 | The amount to send. |
sourceTransferEndpoint.transferEndPointType | Yes | String | programFundingSource | Use programFundingSource. |
sourceTransferEndpoint.identifier | Yes | String | 36 characters | Your disbursement account identifier, issued at onboarding. |
sourceTransferEndpoint.currency | Yes | String | 3-character ISO | Only USD is supported. |
targetTransferEndpoint.transferEndPointType | Yes | String | singlePhaseFunding | Use singlePhaseFunding for this flow. |
targetTransferEndpoint.identifier | Yes | UUID | GUID | Your payee site identifier, issued at onboarding. |
targetTransferEndpoint.currency | Yes | String | 3-character ISO | Only USD is supported. |
targetTransferEndpoint.encryptedCardData | Yes | Object | — | The recipient's card details, encrypted. Fields below. |
targetTransferEndpoint.encryptedUserData | Yes | Object | — | The recipient's identity details, encrypted. Fields below. |
Card data fields (encrypted)
These are the fields inside the encrypted encryptedCardData payload.
| Field | Required | Type | Pattern | Description |
|---|---|---|---|---|
cardNumber | Yes | String | Digits only | The recipient's debit card number. |
expiration | Yes | String | MMYYYY | Card expiration date. |
cvv | No | String | 3–4 digits | Card security code. |
firstName | Yes | String | 2–35 characters; letters, hyphen, space | Cardholder's first name. |
lastName | Yes | String | 2–35 characters; letters, hyphen, space | Cardholder's last name. |
address1 | Yes | String | Max 255 | Cardholder address, line 1. |
address2 | No | String | Max 255 | Cardholder address, line 2. |
city | Yes | String | Max 50; letters, hyphen, space | Cardholder city. |
state | Yes | String | 2 characters | Cardholder state. |
zipCode | Yes | String | 5 digits | Cardholder ZIP code. |
User data fields (encrypted)
These are the fields inside the encrypted encryptedUserData payload.
| Field | Required | Type | Pattern | Description |
|---|---|---|---|---|
firstName | Yes | String | 2–35 characters; letters, hyphen, space | Recipient's first name. |
lastName | Yes | String | 2–35 characters; letters, hyphen, space | Recipient's last name. |
zipCode | Yes | String | 5 digits | Recipient's ZIP code. |
dateOfBirth | Conditional | String | YYYY-MM-DD | Required for some program configurations. Year must be between 1901 and the current year. |
Response — accepted
HTTP/1.1 201 Created
Content-Type: application/json
{
"transfer": {
"transferIdentifier": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
"transferStatus": "Pending"
}
}Response parameters
| Parameter | Type | Description |
|---|---|---|
transfer.transferIdentifier | String | The identifier for this payout — the same value you sent. |
transfer.transferStatus | String | The status when the response was sent. Typically Pending; the final state arrives by webhook. |
errors | Array | Present only on failure. Contains code, subcode, and description. |
Response — validation failure
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"transfer": {
"transferIdentifier": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
"transferStatus": "failed"
},
"errors": [
{
"code": 4200,
"subcode": 945,
"description": "Invalid Request Id"
}
]
}Response — system error
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{
"transfer": {
"transferIdentifier": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
"transferStatus": "failed"
},
"errors": [
{
"code": 4214,
"subcode": 1514,
"description": "system error"
}
]
}A 503 means a timeout or internal error. It does not necessarily mean that no money moved. Retry with the
same transferIdentifier — the platform returns the existing record rather than sending a
second payment.
Response codes
| Code | Description | Explanation |
|---|---|---|
| 201 | Created | The payout was accepted. Wait for the webhook for the final outcome. |
| 200 | Record already exists | Response code 4202, sub-code 1502. You resubmitted a transferIdentifier that already exists; the existing transfer's status is returned. |
| 400 | Validation failed | A field is missing or malformed. The sub-code identifies which. |
| 401 | Unauthorized | Token missing, expired, or out of scope. Request a new token and retry. |
| 503 | System error | Timeout or internal error. Retry with the same transferIdentifier. |
See Response Codes for the full sub-code reference.
Step 2 — Receive the outcome
Green Dot posts the final result of the payout to the endpoint you registered during onboarding.
POST https://{yourEndpoint}/events/SinglePhaseTransfer
Webhook payload
{
"accounts": [
{
"accountIdentifier": "4d5e6f70-8192-a3b4-c5d6-e7f809123456",
"events": [
{
"eventIdentifier": "5e6f7081-92a3-b4c5-d6e7-f80912345678",
"eventType": "singlePhaseTransfer",
"eventDateTime": "2026-01-15T12:00:07.1411753Z",
"singlePhaseTransfer": [
{
"transferIdentifier": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
"transactionAmount": 20.00,
"transferStatus": "Completed",
"transferDateTime": "2026-01-15T12:00:07Z",
"response": {
"code": "0",
"subCode": "0",
"description": "success"
}
}
]
}
]
}
]
}Webhook fields
| Field | Type | Description |
|---|---|---|
accounts[].accountIdentifier | UUID | The account the event relates to. |
events[].eventIdentifier | UUID | Unique identifier for this event delivery. Use it to deduplicate. |
events[].eventType | String | singlePhaseTransfer for this flow. |
events[].eventDateTime | DateTime | When the event was generated. |
singlePhaseTransfer[].transferIdentifier | String | Matches the transferIdentifier you sent. This is how you tie the webhook to your record. |
singlePhaseTransfer[].transactionAmount | Decimal | The payout amount. |
singlePhaseTransfer[].transferStatus | String | Completed, Failed, or Declined. |
singlePhaseTransfer[].transferDateTime | DateTime | When the payout reached this status. |
singlePhaseTransfer[].response | Object | code, subCode, and description explaining the outcome. |
Statuses you will receive
| Status | Meaning | What to do |
|---|---|---|
Completed | The payout succeeded and funds reached the recipient. | Mark the payout complete. |
Failed | A system error occurred. | Investigate the sub-code, then send a new payout with a new transferIdentifier. |
Declined | The payout was refused — ineligible account, limit exceeded, OFAC, or below the minimum amount. | Do not retry blindly. Resolve the underlying reason first; the sub-code tells you which. |
See Transfer Status & Webhooks for handling guidance.
Handling webhooks well
- Respond fast. Acknowledge the delivery, then process asynchronously.
- Deduplicate on
eventIdentifier. Assume a webhook can arrive more than once. - Match on
transferIdentifier, not amount or timestamp. It is the only reliable key. - Do not assume ordering. Treat each event on its own merits.
- Have a fallback. If your endpoint is down, use the status lookup below.
Looking up a payout
Retrieve a payout's current state at any time, whether or not the webhook arrived.
Syntax
GET {baseUrl}/enrollment/v1/api/flex/externalAccounts/{externalAccountId}/transfers/{transferId}
Request parameters
| Parameter | Required | Type | Pattern | Description |
|---|---|---|---|---|
externalAccountId | Yes | UUID | GUID | Path parameter. Your payee site identifier. |
transferId | Yes | UUID | GUID | Path parameter. The transferIdentifier of the payout. |
x-Remapped-Authorization | Yes | String | Bearer {accessToken} | Passed as an HTTP header. |
Request-ID | Yes | UUID | GUID | Passed as an HTTP header. Unique per request. |
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"transfer": {
"transferId": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
"transferStatus": "Completed",
"currency": "USD",
"transactionAmount": "20.00",
"totalFeeAmount": "0.0",
"totalTransactionAmount": "20.00"
}
}Response parameters
| Parameter | Type | Description |
|---|---|---|
transferId | String | The payout identifier. |
transferStatus | String | Current status. |
currency | String | Currency of the payout. |
transactionAmount | String | The amount sent to the recipient. |
totalFeeAmount | String | Fees applied to the payout. |
totalTransactionAmount | String | Amount plus fees. |
Paying many recipients
Each payout is its own call with its own transferIdentifier. To run a batch, iterate your list
and submit one request per recipient. Because outcomes arrive asynchronously, a batch is finished
when every payout has reached a final status, not when the loop finishes.
Reconcile on a schedule: any payout still Pending well past its expected settlement window should
be looked up directly rather than waited on.
Updated about 23 hours ago
