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"
}
}
]
}| Field | Requirement and behavior |
|---|---|
transaction_id | Required, non-empty unique transaction identifier |
percentsMerchantId | Required UUID supplied by Percents; identifies the Omni merchant independently of Plaid's merchant text and IDs |
segment | Required; one of new, loyal, lapsed, or JSON null. Determine the value using the referenced merchant's segmentDefinitions. |
account_id | Required; 100% of transaction account IDs must match an entry in the top-level accounts array |
amount | Required finite number in Plaid transaction units; do not convert dollars to cents |
datetime, authorized_datetime, date, authorized_date | At least one valid value; Percents uses the first available in this order |
merchant_name or name | At least one required; merchant_name is preferred as canonical merchant name |
name | Used as the transaction descriptor when present |
merchant_entity_id | Used as the network merchant ID when present |
iso_currency_code | Defaults to USD when absent |
account mask | Required on the matching account |
account name, official_name, subtype, type | Optional card/account metadata |
payment_channel | in 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.