Skip to main content

Overview

Expensify provides comprehensive invoicing capabilities for businesses. This guide covers invoice management, payment processing, and workspace configuration for invoicing.

Invoice Workspace Configuration

Set up a workspace for invoicing with proper business details.

Checking Invoice Configuration

function hasInvoiceSetup(policy: Policy | undefined): boolean {
    return !!policy?.invoice?.companyName && !!policy?.invoice?.companyWebsite;
}

Getting Primary Invoice Workspace

Identify the primary workspace for sending invoices:
function getPrimaryInvoiceWorkspace(policies: OnyxEntry<OnyxCollection<Policy>>): Policy | undefined {
    const invoicePolicies = Object.values(policies ?? {}).filter(
        (policy) => hasInvoiceSetup(policy) && policy?.type === CONST.POLICY.TYPE.CORPORATE
    );
    
    return invoicePolicies[0];
}

Invoice Transfer Bank Account

Configure the default bank account for receiving invoice payments.
function setInvoicingTransferBankAccount(
    bankAccountID: number,
    policyID: string,
    previousBankAccountID: number
) {
    const parameters: SetInvoicingTransferBankAccountParams = {
        bankAccountID,
        policyID,
    };

    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                invoice: {
                    bankAccount: {
                        transferBankAccountID: bankAccountID,
                    },
                },
            },
        },
    ];

    const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                invoice: {
                    bankAccount: {
                        transferBankAccountID: previousBankAccountID,
                    },
                },
            },
        },
    ];

    API.write(WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT, parameters, {
        optimisticData,
        failureData,
    });
}

Invoice Reports

Identifying Invoice Reports

Check if a report is an invoice:
const isInvoiceReceiverReport = 
    report?.invoiceReceiver && 
    'policyID' in report.invoiceReceiver && 
    report.invoiceReceiver.policyID === policyID;

Invoice Report Structure

type InvoiceReport = {
    reportID: string;
    invoiceReceiver?: {
        policyID: string;
        type: 'policy' | 'individual';
        accountID?: number;
    };
    total: number;
    currency: string;
    created: string;
    status: number;
    stateNum: number;
};

Paying Invoices

Invoice Payment Parameters

Prepare payment parameters for invoice reports:
function getPayMoneyOnSearchInvoiceParams(
    policyID: string | undefined,
    payAsBusiness?: boolean,
    methodID?: number,
    paymentMethod?: PaymentMethod
): Partial<PaymentData> {
    const invoiceParams: Partial<PaymentData> = {
        payAsBusiness,
    };

    if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) {
        invoiceParams.bankAccountID = methodID;
    }

    if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) {
        invoiceParams.fundID = methodID;
    }

    return invoiceParams;
}

Processing Invoice Payments

Handle bulk invoice payment operations:
type InvoicePaymentData = PaymentData & {
    payAsBusiness?: boolean;
    bankAccountID?: number;
    fundID?: number;
};

function payInvoicesOnSearch(
    hash: number,
    invoicePayments: InvoicePaymentData[],
    currentSearchKey?: SearchKey
) {
    const optimisticData: OnyxUpdate[] = invoicePayments.map((payment) => ({
        onyxMethod: Onyx.METHOD.MERGE,
        key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${payment.reportID}`,
        value: {
            isActionLoading: true,
        },
    }));

    const successData: OnyxUpdate[] = invoicePayments.map((payment) => ({
        onyxMethod: Onyx.METHOD.MERGE,
        key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${payment.reportID}`,
        value: {
            isActionLoading: false,
        },
    }));

    if (currentSearchKey) {
        successData.push({
            onyxMethod: Onyx.METHOD.MERGE,
            key: currentSearchKey,
            value: {
                data: Object.fromEntries(
                    invoicePayments.map((payment) => [
                        `${ONYXKEYS.COLLECTION.REPORT}${payment.reportID}`,
                        {statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED}
                    ])
                ),
            },
        });
    }

    const failureData: OnyxUpdate[] = invoicePayments.map((payment) => ({
        onyxMethod: Onyx.METHOD.MERGE,
        key: `${ONYXKEYS.COLLECTION.REPORT}${payment.reportID}`,
        value: {
            errors: getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericPaymentError'),
        },
    }));

    API.write(
        WRITE_COMMANDS.PAY_INVOICES_ON_SEARCH,
        {hash, paymentData: JSON.stringify(invoicePayments)},
        {optimisticData, successData, failureData}
    );
}

Invoice Workspace Setup

Company Information

Set up company details for invoicing:
type InvoiceCompanyData = {
    companyName: string;
    companyWebsite: string;
    companyAddress?: string;
    companyPhone?: string;
    companyEmail?: string;
    taxID?: string;
};

function updateInvoiceCompanyInfo(
    policyID: string,
    companyData: InvoiceCompanyData
) {
    const optimisticData: OnyxUpdate[] = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                invoice: {
                    companyName: companyData.companyName,
                    companyWebsite: companyData.companyWebsite,
                    companyAddress: companyData.companyAddress,
                    companyPhone: companyData.companyPhone,
                    companyEmail: companyData.companyEmail,
                    taxID: companyData.taxID,
                },
                pendingFields: {
                    invoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
                },
            },
        },
    ];

    API.write(WRITE_COMMANDS.UPDATE_INVOICE_COMPANY_INFO, {
        policyID,
        ...companyData,
    }, {optimisticData, successData, failureData});
}

Invoice Creation Workflow

Creating an Invoice

  1. Set up invoice workspace with company details
  2. Configure transfer bank account
  3. Create invoice report
  4. Add expense items
  5. Send to client

Invoice States

Invoices progress through several states:
  • Draft - Being created
  • Sent - Delivered to client
  • Viewed - Client has opened
  • Processing - Payment initiated
  • Paid - Payment completed
  • Overdue - Past due date

Invoice Payment Methods

Clients can pay invoices using:
  • ACH Bank Transfer - Direct bank account transfer
  • Debit Card - Instant card payment
  • Credit Card - Credit card payment (if enabled)
  • External Payment - Mark as paid outside Expensify

Invoice Templates

Customize invoice appearance:
type InvoiceTemplate = {
    logoURL?: string;
    primaryColor?: string;
    accentColor?: string;
    headerText?: string;
    footerText?: string;
    termsAndConditions?: string;
    paymentInstructions?: string;
};

function updateInvoiceTemplate(
    policyID: string,
    template: InvoiceTemplate
) {
    const optimisticData: OnyxUpdate[] = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
            value: {
                invoice: {
                    template,
                },
            },
        },
    ];

    API.write(WRITE_COMMANDS.UPDATE_INVOICE_TEMPLATE, {
        policyID,
        template: JSON.stringify(template),
    }, {optimisticData, successData, failureData});
}

Invoice Reminders

Automate payment reminders:
type InvoiceReminderSettings = {
    enabled: boolean;
    daysBeforeDue?: number;
    daysAfterDue?: number;
    frequency?: 'daily' | 'weekly';
    maxReminders?: number;
};

function configureInvoiceReminders(
    policyID: string,
    settings: InvoiceReminderSettings
) {
    API.write(WRITE_COMMANDS.CONFIGURE_INVOICE_REMINDERS, {
        policyID,
        ...settings,
    });
}

Invoice Analytics

Track invoice performance:
type InvoiceAnalytics = {
    totalInvoicesSent: number;
    totalInvoicesPaid: number;
    totalOutstanding: number;
    averagePaymentTime: number; // in days
    overdueInvoices: number;
    currency: string;
};

Multi-Currency Invoicing

Handle invoices in different currencies:
function createMultiCurrencyInvoice(
    policyID: string,
    amount: number,
    currency: string,
    exchangeRate?: number
) {
    const parameters = {
        policyID,
        amount,
        currency,
        exchangeRate: exchangeRate ?? 1,
        baseCurrency: CONST.CURRENCY.USD,
    };

    API.write(WRITE_COMMANDS.CREATE_INVOICE, parameters);
}

Invoice Status Tracking

Monitor invoice payment status:
function getInvoiceStatus(report: InvoiceReport): string {
    if (report.statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED) {
        return 'paid';
    }
    
    if (report.statusNum === CONST.REPORT.STATUS_NUM.APPROVED) {
        return 'processing';
    }
    
    const dueDate = new Date(report.dueDate);
    const now = new Date();
    
    if (now > dueDate) {
        return 'overdue';
    }
    
    if (report.viewed) {
        return 'viewed';
    }
    
    return 'sent';
}

Best Practices

  • Complete all company information before sending invoices
  • Configure a dedicated bank account for invoice payments
  • Set up professional invoice templates
  • Test the invoice flow before sending to clients
  • Enable multiple payment methods for client convenience
  • Set reasonable payment terms (Net 30, Net 60, etc.)
  • Configure automated payment reminders
  • Monitor invoice aging regularly
  • Use clear, professional invoice templates
  • Include detailed payment instructions
  • Provide multiple payment options
  • Send payment confirmations promptly

Sending & Receiving

Learn about payment operations

Reimbursements

Managing expense reimbursements

Bank Accounts

Connecting payment accounts

Payment Methods

Managing payment methods

Build docs developers (and LLMs) love