Transactions

A transaction record represents a single financial event on a user’s connected bank account. Transaction data is fetched from Plaid by the mining pipeline, converted into FloatMe’s internal format by the refiner Lambda, and stored in DynamoDB.

Transactions are identified by a Plaid-assigned transaction_id (txn_id) that is stable across updates. Amounts are stored in cents.

See Plaid Mining Pipeline for how transactions are fetched, refined, and stored.

Storage

Table: prod-txn-transactions

Region: us-east-2

Keys

Key Value

PK

TXN#<txn_id>

SK

TXN#<txn_id>

GSI1PK

ACCOUNTS#<user_id>

GSI1SK

[REMOVED#]DATE#<year-month-day>#TXN#<txn_id>

GSI2PK

ACCOUNTS#<user_id>#CATEGORY#<category_id>

GSI2SK

[REMOVED#]DATE#<year-month-day>#TXN#<txn_id>

GSI3PK

ACCOUNTS#<user_id>#ACCOUNT#<acct_id>

GSI3SK

[REMOVED#]DATE#<year-month-day>#TXN#<txn_id>

GSI4PK

ACCOUNTS#<user_id>#ACCOUNT#<acct_id>#CATEGORY#<category_id>

GSI4SK

[REMOVED#]DATE#<year-month-day>#TXN#<txn_id>

The [REMOVED#] prefix on GSI sort keys is how soft-deletes work — see Removal below.

Attributes

Attribute Description

type

Always TRANSACTION

user_id

FloatMe user identifier

acct_id

Plaid account identifier for the account this transaction belongs to

txn_id

Plaid-assigned transaction identifier. Stable across updates.

txn_account_owner_id

Owner of the account (used for joint accounts)

txn_pending_transaction_id

For a posted transaction, the txn_id of the pending transaction it settled from (if any)

txn_pending

true if the transaction is still pending (not yet settled)

txn_removed

true if the transaction has been soft-deleted

txn_payment_channel

Channel through which the payment was made: online, in store, other

txn_payment_meta

Additional payment metadata (reference number, PPD ID, payee, payer, etc.)

txn_name

Transaction name as reported by the financial institution

txn_original_description

Raw description string from the financial institution before any enrichment

txn_merchant_name

Cleaned merchant name from Plaid’s enrichment

txn_payment_location

Geographic location of the transaction (address, city, region, lat/long)

txn_authorized_date

RFC 3339 date (YYYY-MM-DD) when the transaction was authorized by the card network

txn_date

RFC 3339 date (YYYY-MM-DD) when the transaction posted to the account

txn_category_id

Plaid legacy category identifier

txn_category

Plaid legacy category hierarchy array (e.g., ["Food and Drink", "Restaurants"])

txn_iso_cc

ISO 4217 currency code (e.g., USD)

txn_unofficial_cc

Unofficial currency code for non-ISO currencies

txn_amount

Transaction amount in cents. Positive = money leaving the account (debit/purchase). Negative = money entering the account (deposit/refund).

txn_amount_string

String representation of the amount (e.g., "12.50")

txn_transaction_code

Plaid transaction code classifying the type of transaction (e.g., bill payment, purchase, transfer)

txn_personal_finance_category

Plaid’s newer personal finance category object with primary and detailed fields (e.g., primary: FOOD_AND_DRINK, detailed: FOOD_AND_DRINK_FAST_FOOD)

merchant_logo_url

URL of the merchant’s logo image from Plaid’s enrichment

merchant_website

Merchant website URL from Plaid’s enrichment

Query Patterns

Query Index Conditions

Get a specific transaction by ID

Primary

PK = TXN#<txn_id>, SK = TXN#<txn_id>

List transactions for a user (sorted by date, newest first)

GSI1

GSI1PK = ACCOUNTS#<user_id>, GSI1SK begins_with DATE#

List transactions for a user filtered by category

GSI2

GSI2PK = ACCOUNTS#<user_id>#CATEGORY#<category_id>, GSI2SK begins_with DATE#

List transactions for a user filtered by account

GSI3

GSI3PK = ACCOUNTS#<user_id>#ACCOUNT#<acct_id>, GSI3SK begins_with DATE#

List transactions for a user filtered by account and category

GSI4

GSI4PK = ACCOUNTS#<user_id>#ACCOUNT#<acct_id>#CATEGORY#<category_id>, GSI4SK begins_with DATE#

Removal

When Plaid signals that transactions have been removed (via a TRANSACTIONS_REMOVED webhook), FloatMe soft-deletes them rather than physically removing the DynamoDB records. This preserves the audit trail while excluding the records from all list queries.

Soft-delete works by prepending REMOVED# to all four GSI sort keys:

Before removal:  DATE#2024-03-15#TXN#abc123
After removal:   REMOVED#DATE#2024-03-15#TXN#abc123

All list queries use begins_with DATE# as the sort key condition, which naturally excludes any record whose SK starts with REMOVED#. The primary key is unchanged, so point lookups by txn_id still return the record (with txn_removed = true).