Skip to content
Last updated

Plaid transaction format

plaid-txn-stream remains available alongside the recommended native format. Upload UTF-8 JSON with fileType=json and Content-Type: application/json.

The payload must be an object containing a transactions array and an accounts array, joined by account_id. Every distinct account_id referenced by a transaction must be present in accounts; partial account coverage fails validation for the entire file.

{
  "accounts": [
    {
      "account_id": "acc_EXAMPLE_abc123xyz",
      "mask": "4321",
      "name": "Freedom Unlimited",
      "official_name": "CHASE FREEDOM UNLIMITED VISA SIGNATURE",
      "type": "credit",
      "subtype": "credit card",
      "balances": {
        "available": 1234.56,
        "current": 1234.56,
        "iso_currency_code": "USD"
      }
    }
  ],
  "transactions": [
    {
      "transaction_id": "plaid-transaction-1",
      "percentsMerchantId": "9b1f2f48-eaf2-4c22-9a49-76d32f942183",
      "segment": "loyal",
      "pending_transaction_id": null,
      "account_id": "acc_EXAMPLE_abc123xyz",
      "amount": 4.75,
      "iso_currency_code": "USD",
      "date": "2026-07-11",
      "datetime": "2026-07-11T14:32:07Z",
      "authorized_date": "2026-07-10",
      "authorized_datetime": "2026-07-10T18:04:12Z",
      "name": "BLUE BOTTLE COFFEE #142 OAKLAND CA",
      "merchant_name": "Blue Bottle Coffee",
      "merchant_entity_id": "mrch_ent_EXAMPLE_bluebottle",
      "payment_channel": "in store",
      "pending": false,
      "category": ["Food and Drink", "Restaurants", "Coffee Shop"],
      "personal_finance_category": {
        "primary": "FOOD_AND_DRINK",
        "detailed": "FOOD_AND_DRINK_COFFEE",
        "confidence_level": "VERY_HIGH"
      },
      "location": {
        "city": "Oakland",
        "region": "CA",
        "postal_code": "94607",
        "country": "US"
      }
    }
  ]
}

Fields used for normalization

FieldRequirement and behavior
transaction_idRequired, non-empty unique transaction identifier
percentsMerchantIdRequired UUID supplied by Percents; identifies the Omni merchant independently of Plaid's merchant text and IDs
segmentRequired; one of new, loyal, lapsed, or JSON null. Determine the value using the referenced merchant's segmentDefinitions.
account_idRequired; 100% of transaction account IDs must match an entry in the top-level accounts array
amountRequired finite number in Plaid transaction units; do not convert dollars to cents
datetime, authorized_datetime, date, authorized_dateAt least one valid value; Percents uses the first available in this order
merchant_name or nameAt least one required; merchant_name is preferred as canonical merchant name
nameUsed as the transaction descriptor when present
merchant_entity_idUsed as the network merchant ID when present
iso_currency_codeDefaults to USD when absent
account maskRequired on the matching account
account name, official_name, subtype, typeOptional card/account metadata
payment_channelin store maps to chip; online maps to online; other values remain in the raw payload without a mapped auth method

Additional Plaid fields are allowed and preserved with the raw transaction for audit and troubleshooting. Do not remove fields from a standard Plaid response merely because Percents does not currently normalize them.

Every transaction in a file must contain the custom percentsMerchantId and segment fields. Percents rejects the complete file before ingestion when a merchant UUID is unknown, a segment is missing or invalid, or any other row fails the public contract.

On validation failure, the FileUploadProcessingResult webhook returns every missing account ID in errorMetadata.missingAccountIds and every unknown merchant UUID in errorMetadata.unknownPercentsMerchantIds.