Send payouts tutorial

Use this how-to guide to see the flow of initiating a payout. A payout is any instance where you need to distribute funds to a payee.

Some examples of payouts include sending money to:

  • Gig workers
  • Contractors
  • Employees

Get your access token

If you are using a server-side integration, you can skip this step.

Create an access token, which you’ll later include as the Moov Drop onboarding.token when onboarding your senders and payees.

1
2
3
4
curl -X POST "https://api.moov.io/oauth2/token" \
  -u "PUBLIC_KEY:PRIVATE_KEY" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode  "scope=/accounts.write" \
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { Moov, SCOPES } from '@moovio/node';

const moov = new Moov({
  accountID: "YOUR_MOOV_ACCOUNT_ID",
  publicKey: "PUBLIC_KEY",
  secretKey: "PRIVATE_KEY",
  domain: "YOUR_DOMAIN"
});

const scopes = [SCOPES.ACCOUNTS_CREATE];
try {
  const {token} = await moov.generateToken(scopes);
  // Do something with token
} catch(err) {
  // Handle any errors
}

Onboard your senders

If you’re sending payouts yourself you can skip this section.

Create accounts for the business users of your platform that will be sending payouts. Your senders will need to be verified. You can do this with the Node SDK, Moov.js, or the pre-built Moov Drop. Explore the options below in the tabs.

Collect the necessary information from your senders using the Node SDK.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// create Moov instance with generated token
const moov = new Moov(credentialsObject);

// create Moov account, likely on form submit
moov.accounts.create({
  accountType: "business",
  profile: {business: {}},
  capabilities: ["send-funds"]
}).then((account) => {
  console.log(account);
}).catch((err) => {
  console.error(err);
});

// Generate a new token server-side with the accountID and update the token within your frontend app. You can chain methods together
moov.setToken("new-token");

// add a representative to account
moov.accounts.representatives.create({ 
  accountID: "newly-created-accountID",
  representative: {}
});

// link a bank account
moov.accounts.bankAccounts.link({
  accountID: "newly-created-accountID",
  bankAccount: {
    holderName: "Name",
    holderType: "business",
    accountNumber: "0004321567000",
    routingNumber: "123456789",
    bankAccountType: "checking"
  }
}).then((bankAccount) => {
// kick off micro-deposit verification
  moov.accounts.bankAccounts.startMicroDepositVerification(accountID, bankAccountID);
});

// Later, verify micro-deposits
moov.accounts.bankAccounts.completeMicroDepositVerification(accountID, bankAccountID, [12, 45]);

// Asychronously, the ach-debit-fund payment method will be created from this verified bank account. If you want to move funds out of the Moov wallet,  this bank account can be the destination of a wallet-to-bank transfer.

Collect the necessary information from your senders using Moov.js.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// create Moov instance with generated token
const moov = Moov(token);

// create Moov account, likely on form submit
moov.accounts.create({
  accountType: "business",
  profile: {business: {}},
  capabilities: ["send-funds"]
}).then((account) => {
  console.log(account);
}).catch((err) => {
  console.error(err);
});

// Generate a new token server-side with the accountID and update the token within your frontend app. You can chain methods together
moov.setToken("new-token");

// add a representative to account
moov.accounts.representatives.create({ 
  accountID: "newly-created-accountID",
  representative: {}
});

// link a bank account
moov.accounts.bankAccounts.link({
  accountID: "newly-created-accountID",
  bankAccount: {
    holderName: "Name",
    holderType: "business",
    accountNumber: "0004321567000",
    routingNumber: "123456789",
    bankAccountType: "checking"
  }
}).then((bankAccount) => {
// kick off micro-deposit verification
  moov.accounts.bankAccounts.startMicroDepositVerification({accountID, bankAccountID});
});

// Later, verify micro-deposits
moov.accounts.bankAccounts.completeMicroDepositVerification({accountID, bankAccountID, [12, 45]});

// Asychronously, the ach-debit-fund payment method will be created from this verified bank account. If you want to move funds out of the Moov wallet,  this bank account can be the destination of a wallet-to-bank transfer.

Collect the necessary information from your senders via a pre-built UI component.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Onboarding Moov Drop

const onboarding = document.querySelector("moov-onboarding");
// After generating a token, set it on the onboarding element
onboarding.token = "some-generated-token";

// Include your own accountID which can be found in the Moov Dashboard
onboarding.facilitatorAccountID = "your-account-id";

// send-funds capability needed for this flow
onboarding.capabilities = ["send-funds"];

// Funding will occur with a bank account
onboarding.paymentMethodTypes = ["bankAccount"];

// Verify bank account with microdeposits
onboarding.microDeposits = true;
// Follow the Onboarding Moov Drops and Plaid guides if linking bank accounts using Plaid

// Open the onboarding flow when ready
onboarding.open = true;

Onboard payees

You’ll need to create an account for each recipient and link their bank account. For the purposes of receiving the payouts, bank account verification via micro-deposits for the payees not required. Similar to how you would onboard your senders, you can do this with the Node SDK, Moov.js, or the pre-built Moov Drop. Explore the options below in the tabs.

Collect the necessary information from your senders using the Node SDK.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// create Moov instance with generated token
const moov = new Moov(credentialsObject);

// create Moov account, likely on form submit
moov.accounts.create({
  accountType: "business",
  profile: {business: {}},
  capabilities: ["transfers", "collect-funds"]
}).then((account) => {
  console.log(account);
}).catch((err) => {
  console.error(err);
});

// Generate a new token server-side with the accountID and update the token within your frontend app. You can chain methods together
moov.setToken("new-token");

// add a representative to account
moov.accounts.representatives.create({ 
  accountID: "newly-created-accountID",
  representative: {}
});

// link a bank account
moov.accounts.bankAccounts.link({
  accountID: "newly-created-accountID",
  bankAccount: {
    holderName: "Name",
    holderType: "business",
    accountNumber: "0004321567000",
    routingNumber: "123456789",
    bankAccountType: "checking"
  }
}).then((bankAccount) => {
// kick off micro-deposit verification
  moov.accounts.bankAccounts.startMicroDepositVerification(accountID, bankAccountID);
});

// Later, verify micro-deposits
moov.accounts.bankAccounts.completeMicroDepositVerification(accountID, bankAccountID, [12, 45]);

// Asynchronously, the ach-debit-fund payment method will be created from this verified bank account. If you want to move funds out of the Moov wallet,  this bank account can be the destination of a wallet-to-bank transfer.

Collect the necessary information from your senders using Moov.js.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// create Moov instance with generated token
const moov = Moov(token);

// create Moov account, likely on form submit
moov.accounts.create({
  accountType: "business",
  profile: {business: {}},
  capabilities: ["transfers", "collect-funds"]
}).then((account) => {
  console.log(account);
}).catch((err) => {
  console.error(err);
});

// Generate a new token server-side with the accountID and update the token within your frontend app. You can chain methods together
moov.setToken("new-token");

// add a representative to account
moov.accounts.representatives.create({ 
  accountID: "newly-created-accountID",
  representative: {}
});

// link a bank account
moov.accounts.bankAccounts.link({
  accountID: "newly-created-accountID",
  bankAccount: {
    holderName: "Name",
    holderType: "business",
    accountNumber: "0004321567000",
    routingNumber: "123456789",
    bankAccountType: "checking"
  }
}).then((bankAccount) => {
// kick off micro-deposit verification
  moov.accounts.bankAccounts.startMicroDepositVerification({accountID, bankAccountID});
});

// Later, verify micro-deposits
moov.accounts.bankAccounts.completeMicroDepositVerification({accountID, bankAccountID, [12, 45]});

// Asynchronously, the ach-debit-fund payment method will be created from this verified bank account. If you want to move funds out of the Moov wallet,  this bank account can be the destination of a wallet-to-bank transfer.

Collect the necessary information from your senders via a pre-built UI component.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Onboarding Moov Drop

const onboarding = document.querySelector("moov-onboarding");
// After generating a token, set it on the onboarding element
onboarding.token = "some-generated-token";

// Include your own accountID which can be found in the Moov Dashboard
onboarding.facilitatorAccountID = "your-account-id";

// Transfers and collect-funds capabilities are needed for this flow
onboarding.capabilities = ["transfers", "collect-funds"];

// Funding will occur with a bank account
onboarding.paymentMethodTypes = ["bankAccount"];

// Verify bank account with micro-deposits
onboarding.microDeposits = true;
// Follow the Onboarding Moov Drops and Plaid guides if linking bank accounts using Plaid

// Open the onboarding flow when ready
onboarding.open = true;

Send a payout

To send a payout, create a transfer from a verified sender to a recipient.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
curl -X POST "https://api.moov.io/transfers" \
  -H "Authorization: Bearer {token}" \
  -H "X-Idempotency-Key: UUID" \
  -H "X-Wait-For: rail-response" \
  --data-raw '{
    "source": {
      "paymentMethodID": "UUID"
    },
    "destination": {
      "paymentMethodID": "UUID"
    },
    "amount": {
      "value": 3215, // $3.15
      "currency": "USD"
    },
    "description": "Payout example"
  }'\
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import { Moov } from '@moovio/node';

const moov = new Moov(credentialsObject);

try {
  const transfer = {
    source: { paymentMethodID: "..." },
    destination: { paymentMethodID: "..." },
    amount: {
      value: 3215, // $32.15
      currency: "USD"
    },
    description: "Payout example"
  };
  const { transferID } = moov.transfers.create(transfer);
} catch (err) {
  // ...
}

The sender’s bank account will be the source of funds, so we’ll need the ach-debit-fund payment method that was created after they linked their bank account and submitted all capability requirements.

The payee’s bank account will be the destination for funds. We’ll use the ach-credit-sameday payment method that was created after their bank account was linked.

Track when the payment will land

This is optional, but if you want to know the status of the transfer, you can subscribe to the transfer.updated webhook event in the Moov Dashboard. This will keep you informed on what stage the transfer is at (read more for a granular list of transfer statuses).

For a sense of the ACH same-day transfer timing, see the diagram below. The payment is processed the same day, then funds will be made available later to account for possible returns or processing issues.

ACH same-day processing flow diagram
Summary Beta