Overview
Expensify supports importing transactions from both personal cards and company card programs. This allows for comprehensive expense tracking across all payment methods.Personal Cards
Setting Card Reimbursability
Control whether transactions from a personal card are eligible for reimbursement.function setPersonalCardReimbursable(
cardID: number,
reimbursable: boolean,
previousValue?: boolean
) {
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
reimbursable,
pendingFields: {
reimbursable: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
reimbursable: null,
},
},
},
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
...(previousValue === undefined ? {} : {reimbursable: previousValue}),
pendingFields: {
reimbursable: null,
},
errorFields: {
reimbursable: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
},
];
const parameters: SetPersonalCardReimbursableParams = {
cardID,
reimbursable,
};
API.write(WRITE_COMMANDS.SET_PERSONAL_CARD_REIMBURSABLE, parameters, {
optimisticData,
finallyData,
failureData,
});
}
Syncing Card Transactions
Manually trigger a sync to fetch the latest transactions from a connected card.function syncCard(
cardID: number,
lastScrapeResult?: number,
breakConnection?: boolean
) {
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
isLoadingLastUpdated: true,
lastScrapeResult: CONST.JSON_CODE.SUCCESS,
pendingFields: {
lastScrape: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
lastScrape: null,
},
},
},
},
];
const finallyData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
isLoadingLastUpdated: false,
pendingFields: {
lastScrape: null,
},
},
},
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
lastScrapeResult,
isLoadingLastUpdated: false,
pendingFields: {
lastScrape: null,
},
errorFields: {
lastScrape: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
},
];
const parameters: {cardID: number; breakConnection?: number} = {
cardID,
};
if (breakConnection) {
// Simulate "Account not found" error code for testing
parameters.breakConnection = 434;
}
API.write(WRITE_COMMANDS.SYNC_CARD, parameters, {
optimisticData,
finallyData,
failureData,
});
}
Unassigning Cards
Remove a personal card from the account.function unassignCard(card: Card) {
const cardID = card.cardID;
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
},
},
},
];
const successData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: null,
},
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
pendingAction: null,
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
];
const parameters: UnassignCardParams = {
cardID,
};
API.write(WRITE_COMMANDS.UNASSIGN_CARD, parameters, {
optimisticData,
successData,
failureData,
});
}
Deleting Personal Cards
Permanently remove a personal card and its associated transactions.type DeletePersonalCardData = {
cardID: number;
card?: Card;
allTransactions: OnyxCollection<Transaction>;
allReports: OnyxCollection<Report>;
savedColumnLayout?: SavedCSVColumnLayoutData;
};
function deletePersonalCard({
cardID,
card,
allTransactions,
allReports,
savedColumnLayout
}: DeletePersonalCardData) {
// Find all transactions associated with this card that are on open/unsubmitted reports
// This matches the backend logic which only deletes transactions on open reports
const transactionsToDelete: Transaction[] = [];
for (const transaction of Object.values(allTransactions ?? {})) {
if (transaction?.cardID === cardID && isReportOpenOrUnsubmitted(transaction.reportID, allReports)) {
transactionsToDelete.push(transaction);
}
}
// Optimistically remove the card and its saved column layout immediately for instant UI feedback
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST | typeof ONYXKEYS.NVP_SAVED_CSV_COLUMN_LAYOUT_LIST | typeof ONYXKEYS.COLLECTION.TRANSACTION>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: null,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_SAVED_CSV_COLUMN_LAYOUT_LIST,
value: {
[cardID]: null,
},
},
];
// Optimistically delete transactions and prepare failure data to restore them
for (const transaction of transactionsToDelete) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`,
value: null,
});
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`,
value: transaction,
});
}
const parameters: DeletePersonalCardParams = {
cardID,
};
API.write(WRITE_COMMANDS.DELETE_PERSONAL_CARD, parameters, {
optimisticData,
failureData,
});
}
Deleting a personal card will also remove all associated transactions from open/unsubmitted reports. This action cannot be undone.
Updating Card Details
Card Name
Customize the display name for a card.function updateAssignedCardName(
cardID: string,
newCardTitle: string,
oldCardTitle?: string
) {
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST | typeof ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
nameValuePairs: {
cardTitle: newCardTitle,
pendingFields: {
cardTitle: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
cardTitle: null,
},
},
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES,
value: {
[cardID]: newCardTitle,
},
},
];
const parameters: UpdateCompanyCardNameParams = {
cardID: Number(cardID),
cardName: newCardTitle,
};
API.write(WRITE_COMMANDS.UPDATE_COMPANY_CARD_NAME, parameters, {
optimisticData,
finallyData,
failureData,
});
}
Transaction Start Date
Set the date from which transactions should be imported.function updateAssignedCardTransactionStartDate(
cardID: string,
newStartDate: string,
oldStartDate?: string
) {
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.CARD_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CARD_LIST,
value: {
[cardID]: {
scrapeMinDate: newStartDate,
pendingFields: {
scrapeMinDate: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
scrapeMinDate: null,
},
},
},
},
];
const parameters: UpdateCardTransactionStartDateParams = {
cardID: Number(cardID),
startDate: newStartDate,
};
API.write(WRITE_COMMANDS.UPDATE_CARD_TRANSACTION_START_DATE, parameters, {
optimisticData,
finallyData,
failureData,
});
}
Continuous Reconciliation
Enable automatic reconciliation with accounting integrations.function toggleContinuousReconciliation(
workspaceAccountID: number,
shouldUseContinuousReconciliation: boolean,
connectionName: ConnectionName,
oldConnectionName?: ConnectionName
) {
const parameters = shouldUseContinuousReconciliation
? {
workspaceAccountID,
shouldUseContinuousReconciliation,
expensifyCardContinuousReconciliationConnection: connectionName,
}
: {
workspaceAccountID,
shouldUseContinuousReconciliation,
};
const optimisticData: Array<
OnyxUpdate<typeof ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION | typeof ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION>
> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
value: {
value: shouldUseContinuousReconciliation,
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
value: connectionName,
},
];
API.write(WRITE_COMMANDS.TOGGLE_CARD_CONTINUOUS_RECONCILIATION, parameters, {
optimisticData,
successData,
failureData,
});
}
Issuing New Cards
Starting the Card Flow
Initiate the process for issuing a new card to a member.function startIssueNewCardFlow(policyID: string | undefined) {
const parameters: StartIssueNewCardFlowParams = {
policyID,
};
API.read(READ_COMMANDS.START_ISSUE_NEW_CARD_FLOW, parameters);
}
Configuring Expensify Cards
Set up Expensify Card program for a workspace.function configureExpensifyCardsForPolicy(
policyID: string,
workspaceAccountID: number,
bankAccountID?: number
) {
if (!bankAccountID) {
return;
}
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS | typeof ONYXKEYS.COLLECTION.EXPENSIFY_CARD_BANK_ACCOUNT_METADATA>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
value: {
isLoading: true,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_BANK_ACCOUNT_METADATA}${workspaceAccountID}`,
value: {
isLoading: true,
isSuccess: false,
},
},
];
const parameters = {
policyID,
bankAccountID,
};
API.write(WRITE_COMMANDS.CONFIGURE_EXPENSIFY_CARDS_FOR_POLICY, parameters, {
optimisticData,
successData,
failureData,
});
}
Related Resources
Overview
Learn about Expensify Card basics
Card Management
Managing card limits and operations
