Pay out money

At Moov, we think of payouts as any instance where you need to distribute funds to a payee. For example, you could be sending money to gig workers, contractors, or employees. In this guide, we cover the flow for initiating payouts with Moov.

Install Moov.js

If you’re planning to use the API via our Node SDK to onboard users, you can skip this section.

If you’re looking to use Moov Drops or Moov.js to onboard your senders, start by installing Moov.js. There are two different ways you can install Moov.js. You can do a script based installation, or initialize the package via npm.

Include the script tag

1
2
3
4
<script type="text/javascript" src="https://js.moov.io/v1"></script>
<script>
  const moov = Moov(token)
</script>

Install via npm

1
npm i @moovio/moov-js
1
2
3
4
import { loadMoov } from '@moovio/moov-js';

const moovAccessToken = await fetch(...); // Use your server to get a Moov access token with appropriate scopes
const moov = await loadMoov(moovAccessToken);

Get your access token

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
 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
}
1
2
3
4
5
curl -X POST "https://api.moov.io/oauth2/token" \
  -u "client_id:client_secret" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode  "scope=/accounts.write" \

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 be subject to KYC verification. You have three different options for doing this:

Option 1: Onboarding Moov Drop Collect the necessary information from your senders via a pre-built UI component.

Option 2: Moov.js with your own UI Collect the necessary information from your senders using Moov.js

Option 3: API via Node SDK When using our server-side Node SDK to onboard a sender, you can use the same code as you would when using Moov.js because the method names are the same.

 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 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;
 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
43
44
45
// create Moov instance with generated token
const moov = Moov('some-generated-token');

// create Moov account, likely on form submit
moov.accounts.create({
	accountType: 'business',
	profile: {
		business: {}
	},
	capabilities: ['transfers', '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.

 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
43
44
45
// create Moov instance with generated token
const moov = Moov('some-generated-token');

// create Moov account, likely on form submit
moov.accounts.create({
	accountType: 'business',
	profile: {
		business: {}
	},
	capabilities: ['transfers', '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.

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 have three different options for onboarding payees:

Option 1: Onboarding Moov Drop Collect the necessary information from the payees via a pre-built UI component.

Option 2: Moov.js with your own UI Collect the necessary information from the payees using Moov.js

Option 3: API via Node SDK When using our server-side Node SDK to onboard a payee, you can use the same code as you would when using Moov.js because the method names are the same.

 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 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;
 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
43
44
45
// create Moov instance with generated token
const moov = Moov('some-generated-token');

// create Moov account, likely on form submit
moov.accounts.create({
	accountType: 'business',
	profile: {
		business: {}
	},
	capabilities: ['transfers', '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.

 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
43
44
45
// create Moov instance with generated token
const moov = Moov('some-generated-token');

// create Moov account, likely on form submit
moov.accounts.create({
	accountType: 'business',
	profile: {
		business: {}
	},
	capabilities: ['transfers', '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.

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
18
19
20
21
22
23
24
25
// Authenticate
import { Moov } from '@moovio/node';

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

// Server-side Node SDK example
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) {
  // ...
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
curl -X GET https://api.moov.io/transfers \
  -H 'Authorization: Bearer {token}' \
  -H "Origin: https://api.moov.io" \
  --data-raw '{
    "source": {
      "paymentMethodID": "UID",
    },
    "destination": {
      "paymentMethodID": "UID",
    },
    "amount": {
      "value": 3215, // $3.15
      "currency": "USD"
    },
    "description": "Payout example"
  }'

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 completed KYC.

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

What’s next

If you’re interested in other use cases, you can dive into our guides: