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.

1. 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
}

2. 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 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
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.
 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.
 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;

3. 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
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.
 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.
 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;

4. 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.

5. 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

What’s next

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

Summary Beta