Skip to main content

Overview

Expensify supports automated expense reimbursement workflows with flexible policy configurations. This guide covers setting up reimbursement policies, processing payments, and managing reimbursement accounts.

Reimbursement Choices

Expensify offers several reimbursement options for workspace policies:
const REIMBURSEMENT_CHOICES = {
    REIMBURSEMENT_YES: 'reimburseYes',
    REIMBURSEMENT_NO: 'reimburseNo',
    REIMBURSEMENT_MANUAL: 'reimburseManual',
};

Reimbursement Types

  • Direct Reimbursement (reimburseYes) - Automatic ACH reimbursement to employee bank accounts
  • No Reimbursement (reimburseNo) - Expenses tracked only, no automatic payment
  • Manual Reimbursement (reimburseManual) - Mark as paid outside Expensify

Setting Workspace Reimbursement

Configure the reimbursement method for a workspace policy.
function setWorkspaceReimbursement(
    policyID: string,
    reimbursementChoice: ValueOf<typeof CONST.POLICY.REIMBURSEMENT_CHOICES>,
    bankAccountID?: number,
    policy?: Policy,
    shouldUpdateLastUsedPaymentMethod = false,
) {
    const optimisticData: OnyxUpdate[] = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                reimbursementChoice,
                autoReimbursement: {
                    limit: reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES 
                        ? policy?.autoReimbursement?.limit ?? CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT 
                        : 0,
                },
                errorFields: {reimbursementChoice: null},
                pendingFields: {reimbursementChoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
            },
        },
    ];

    const successData: OnyxUpdate[] = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                errorFields: {reimbursementChoice: null},
                pendingFields: {reimbursementChoice: null},
            },
        },
    ];

    const failureData: OnyxUpdate[] = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                reimbursementChoice: policy?.reimbursementChoice ?? null,
                autoReimbursement: {
                    limit: policy?.autoReimbursement?.limit,
                },
                errorFields: {
                    reimbursementChoice: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
                },
                pendingFields: {reimbursementChoice: null},
            },
        },
    ];

    // Update last used payment method if needed
    if (shouldUpdateLastUsedPaymentMethod) {
        optimisticData.push({
            onyxMethod: Onyx.METHOD.MERGE,
            key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD,
            value: {
                [policyID]: {
                    expense: {
                        name: CONST.IOU.PAYMENT_TYPE.VBBA,
                        bankAccountID,
                    },
                },
            },
        });
    }

    const params: SetWorkspaceReimbursementParams = {
        policyID,
        reimbursementChoice,
        bankAccountID,
    };

    API.write(WRITE_COMMANDS.SET_WORKSPACE_REIMBURSEMENT, params, {
        optimisticData,
        successData,
        failureData,
    });
}

Currency Support

Direct Reimbursement Currencies

Check if a currency supports direct reimbursement:
function isCurrencySupportedForDirectReimbursement(currency: string): boolean {
    return [
        CONST.CURRENCY.USD,
        CONST.CURRENCY.AUD,
        CONST.CURRENCY.GBP,
    ].includes(currency);
}

Global Reimbursement Currencies

Check if a currency supports global reimbursement (via Corpay):
function isCurrencySupportedForGlobalReimbursement(currency: string): boolean {
    return ![
        CONST.CURRENCY.USD,
        CONST.CURRENCY.AUD,
        CONST.CURRENCY.GBP,
    ].includes(currency);
}

Reimbursement Accounts

Verified Business Bank Account (VBBA)

A VBBA is required for direct reimbursement in the US.
function getVBBADataForOnyx(
    currentStep?: BankAccountStep,
    shouldShowLoading = true
): OnyxData<typeof ONYXKEYS.REIMBURSEMENT_ACCOUNT | typeof ONYXKEYS.NVP_LAST_PAYMENT_METHOD> {
    return {
        optimisticData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                value: {
                    isLoading: shouldShowLoading,
                    errors: null,
                },
            },
        ],
        successData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                value: {
                    isLoading: false,
                    errors: null,
                    // Store the current step to update draft data
                    draftStep: currentStep,
                },
            },
        ],
        failureData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                value: {
                    isLoading: false,
                    errors: getMicroSecondOnyxErrorWithTranslationKey('walletPage.addBankAccountFailure'),
                },
            },
        ],
    };
}

Corpay Accounts (International)

For global reimbursements outside the US, UK, and Australia:
function createCorpayBankAccount(
    fields: ReimbursementAccountForm,
    policyID: string | undefined
) {
    const parameters = {
        type: 1,
        isSavings: false,
        isWithdrawal: true,
        inputs: JSON.stringify(fields),
        policyID,
    };

    const onyxData: OnyxData<typeof ONYXKEYS.REIMBURSEMENT_ACCOUNT> = {
        optimisticData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                value: {
                    isLoading: true,
                    isCreateCorpayBankAccount: true,
                },
            },
        ],
        successData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                value: {
                    isLoading: false,
                    isCreateCorpayBankAccount: false,
                    errors: null,
                    isSuccess: true,
                },
            },
        ],
        failureData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                value: {
                    isLoading: false,
                    isCreateCorpayBankAccount: false,
                    isSuccess: false,
                },
            },
        ],
    };

    return API.write(WRITE_COMMANDS.BANK_ACCOUNT_CREATE_CORPAY, parameters, onyxData);
}

Enable Global Reimbursements

For US bank accounts that want to enable global reimbursement capabilities:
function enableGlobalReimbursementsForUSDBankAccount(
    parameters: EnableGlobalReimbursementsForUSDBankAccountParams
) {
    const onyxData: OnyxData<typeof ONYXKEYS.FORMS.ENABLE_GLOBAL_REIMBURSEMENTS> = {
        optimisticData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.FORMS.ENABLE_GLOBAL_REIMBURSEMENTS,
                value: {
                    isEnablingGlobalReimbursements: true,
                    errors: null,
                },
            },
        ],
        successData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.FORMS.ENABLE_GLOBAL_REIMBURSEMENTS,
                value: {
                    isEnablingGlobalReimbursements: false,
                    isSuccess: true,
                },
            },
        ],
        failureData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.FORMS.ENABLE_GLOBAL_REIMBURSEMENTS,
                value: {
                    isEnablingGlobalReimbursements: false,
                    isSuccess: false,
                    errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
                },
            },
        ],
    };

    return API.write(
        WRITE_COMMANDS.ENABLE_GLOBAL_REIMBURSEMENTS_FOR_USD_BANK_ACCOUNT,
        parameters,
        onyxData
    );
}

Auto-Reimbursement

Configure automatic reimbursement limits and frequency.
const autoReimbursementConfig = {
    limit: CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT, // Default: $20,000 USD
    interval: 'weekly', // or 'daily', 'monthly'
};

// When enabling direct reimbursement
const optimisticData = [
    {
        onyxMethod: Onyx.METHOD.MERGE,
        key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
        value: {
            reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES,
            autoReimbursement: {
                limit: autoReimbursementConfig.limit,
            },
        },
    },
];

Report Status After Reimbursement

When a report is reimbursed, its status updates:
const predictedNextStatus = 
    policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO 
        ? CONST.REPORT.STATUS_NUM.CLOSED 
        : CONST.REPORT.STATUS_NUM.OPEN;

Checking Payment Availability

Determine if reimbursement is available for a report:
const arePaymentsDisabled = 
    policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO;

Last Payment Method Tracking

Track the last payment method used for each policy:
type LastPaymentMethodType = {
    expense?: {
        name: string;
        bankAccountID?: number;
    };
    iou?: {
        name: string;
        bankAccountID?: number;
    };
    lastUsed?: {
        name: string;
        bankAccountID?: number;
    };
};

// Update when payment method changes
const onyxData = [
    {
        onyxMethod: Onyx.METHOD.MERGE,
        key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD,
        value: {
            [policyID]: {
                expense: {
                    name: CONST.IOU.PAYMENT_TYPE.VBBA,
                    bankAccountID: selectedBankAccountID,
                },
                lastUsed: {
                    name: CONST.IOU.PAYMENT_TYPE.VBBA,
                    bankAccountID: selectedBankAccountID,
                },
            },
        },
    },
];

Reimbursement Workflows

Standard Reimbursement Flow

  1. Employee submits expense report
  2. Manager approves report
  3. Report moves to processing
  4. ACH transfer initiated
  5. Report marked as reimbursed
  6. Employee receives payment (1-3 business days)

Manual Reimbursement Flow

  1. Employee submits expense report
  2. Manager approves report
  3. Payment processed outside Expensify
  4. Report manually marked as paid
  5. Report closed

Sharing Bank Accounts

Allow multiple admins to access a business bank account:
function shareBankAccount(bankAccountID: number, emailList: string[]) {
    const parameters: ShareBankAccountParams = {
        bankAccountID,
        emailList,
    };

    const onyxData: OnyxData<typeof ONYXKEYS.SHARE_BANK_ACCOUNT> = {
        optimisticData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.SHARE_BANK_ACCOUNT,
                value: {
                    isLoading: true,
                    errors: null,
                },
            },
        ],
        successData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.SHARE_BANK_ACCOUNT,
                value: {
                    isLoading: false,
                    shouldShowSuccess: true,
                },
            },
        ],
        failureData: [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.SHARE_BANK_ACCOUNT,
                value: {
                    isLoading: false,
                    errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
                },
            },
        ],
    };

    API.write(WRITE_COMMANDS.SHARE_BANK_ACCOUNT, parameters, onyxData);
}

Best Practices

  • Set appropriate auto-reimbursement limits
  • Choose the right reimbursement method for your organization
  • Verify bank account information before enabling reimbursements
  • Test the reimbursement flow with a small amount first
  • Use Corpay for non-US/UK/AU currencies
  • Verify country-specific banking requirements
  • Account for currency conversion fees
  • Plan for longer processing times
  • Limit bank account access to necessary admins
  • Regularly audit reimbursement settings
  • Monitor for suspicious reimbursement patterns
  • Keep bank account information up to date

Sending & Receiving

Learn about payment operations

Bank Accounts

Connecting reimbursement accounts

Invoices

Invoice payment workflows

Build docs developers (and LLMs) love