Skip to main content

Introduction

Expensify Cards provide a powerful way to manage company spending with real-time controls, limits, and automated expense tracking. This guide covers the core functionality for issuing, managing, and monitoring Expensify Cards.

Card Types

Expensify supports two types of cards:

Virtual Cards

Virtual cards are issued instantly and can be used immediately for online purchases. They provide enhanced security for digital transactions.
function issueExpensifyCard(
    domainAccountID: number,
    policyID: string | undefined,
    feedCountry: string,
    validateCode: string,
    timeZone: SelectedTimezone | undefined,
    data?: IssueNewCardData,
) {
    if (!data) {
        return;
    }

    const {assigneeEmail, limit, limitType, cardTitle, cardType, validFrom, validThru} = data;

    // ... optimistic data setup

    if (cardType === CONST.EXPENSIFY_CARD.CARD_TYPE.PHYSICAL) {
        API.write(
            WRITE_COMMANDS.CREATE_EXPENSIFY_CARD,
            {...parameters, feedCountry},
            {
                optimisticData,
                successData,
                failureData,
            },
        );
        return;
    }

    // Virtual card creation
    API.write(
        WRITE_COMMANDS.CREATE_ADMIN_ISSUED_VIRTUAL_CARD,
        {
            ...parameters,
            policyID,
            validFrom: validFrom ? DateUtils.normalizeDateToStartOfDay(validFrom, timeZone) : undefined,
            validThru: validThru ? DateUtils.normalizeDateToEndOfDay(validThru, timeZone) : undefined,
        },
        {
            optimisticData,
            successData,
            failureData,
        },
    );
}

Physical Cards

Physical cards are mailed to the cardholder and must be activated before use.
function activatePhysicalExpensifyCard(cardLastFourDigits: string, cardID: number) {
    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: ONYXKEYS.CARD_LIST,
            value: {
                [cardID]: {
                    errors: null,
                    isLoading: true,
                },
            },
        },
    ];

    // ... success and failure data setup

    const parameters: ActivatePhysicalExpensifyCardParams = {
        cardLastFourDigits,
        cardID,
    };

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

Card States

Cards can exist in multiple states:
  • OPEN - Active and ready to use
  • STATE_SUSPENDED - Temporarily frozen
  • CLOSED - Deactivated permanently

Revealing Card Details

For security, card details (PAN, CVV, expiration) are only accessible through a secure API call with validation.
function revealVirtualCardDetails(cardID: number, validateCode: string): Promise<ExpensifyCardDetails> {
    return new Promise((resolve, reject) => {
        const parameters: RevealExpensifyCardDetailsParams = {cardID, validateCode};

        const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.ACCOUNT>> = [
            {
                onyxMethod: Onyx.METHOD.MERGE,
                key: ONYXKEYS.ACCOUNT,
                value: {isLoading: true},
            },
        ];

        // This uses makeRequestWithSideEffects for security
        // Card details cannot be persisted in Onyx
        API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.REVEAL_EXPENSIFY_CARD_DETAILS, parameters, {
            optimisticData,
            successData,
            failureData,
        })
            .then((response) => {
                if (response?.jsonCode !== CONST.JSON_CODE.SUCCESS) {
                    if (response?.jsonCode === CONST.JSON_CODE.INCORRECT_MAGIC_CODE) {
                        reject('validateCodeForm.error.incorrectMagicCode');
                        return;
                    }
                    // ... error handling
                }
                resolve(response as ExpensifyCardDetails);
            })
            .catch(() => reject('cardPage.cardDetailsLoadingFailure'));
    });
}
Card details are never persisted in the client for security reasons. Each time you need to view card details, you must request them from the server with a validation code.

Settlement Configuration

Configure how and when card balances are settled:
function updateSettlementFrequency(
    workspaceAccountID: number,
    settlementFrequency: ValueOf<typeof CONST.EXPENSIFY_CARD.FREQUENCY_SETTING>,
    currentFrequency?: Date
) {
    const monthlySettlementDate = settlementFrequency === CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY 
        ? null 
        : new Date();

    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS>> = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
            value: {
                monthlySettlementDate,
            },
        },
    ];

    const parameters = {
        workspaceAccountID,
        settlementFrequency,
    };

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

Card Management

Learn about freezing, unfreezing, and managing card limits

Company Cards

Integrate and manage company card feeds

Best Practices

  • Always use validation codes when accessing card details
  • Implement proper error handling for fraud detection
  • Monitor card states and freeze suspicious cards immediately
  • Provide clear feedback during card activation
  • Show loading states during card operations
  • Handle offline scenarios gracefully

Build docs developers (and LLMs) love