Sharing first-party transactions
Sharing batches of transactions with Pinwheel for ingestion and bill identification.
Overview
In addition to Bill Navigator detecting and managing bills from externally linked accounts, first-party transaction data can also be shared and used to reflect bills paid from internal accounts.
Transactions can be sent for multiple users without a link token needing to be created. Each transaction should be tied to an end_user_id who has, or will, have a link token created for Bill Navigator.
Formatting transactions
Transactions should be collected into TSV files with the following fields in this order, without a header:
transaction_id– your unique identifier for the transactionend_user_id– your unique identifier for the account holderinstitution_id– for internal accounts, use “internal”account_name– a display name for the accountaccount_type– one of “depository”, “credit”, “other”account_subtype– eg, "checking", "savings"account_mask– the masked account number. This should be the last 4-8 digits of the account number to differentiate it to the holderaccount_id– your unique identifier for the accounttransaction_datetime– timestamp (UTC) when the transaction settledamount– decimal amount. This should be a negative number for outgoings and positive for incomingscurrency_code(optional) – this should be a 3 letter ISO 4217 code. Defaults to “USD” if blankdescription– the transaction descriptioncountry_code(optional) – this should be a 2 letter ISO 3166-1 alpha-2 code. Defaults to “US” if blankpayment_channel(optional) – one of “online”, “in store”, “other”. Defaults to “other” if blanktransfer_type(optional) – "debit" (-) or "credit" (+). This overrides the signage ofamounttransaction_status– one of “pending”, “posted”, “settled”, “funds_available”, “cancelled”, “failed”, “returned”. As updating transactions is not currently supported it is recommended that only final state transactions be included.posted_datetime– timestamp (UTC) when the transaction postedmerchant_category_code– 4 digit merchant category codeenriched_transaction_details(optional) – this should be a JSON object of any additional details related to the transaction. Use the equivalent ofjq -ccompact form.
Rows can be best processed when grouped by end_user_id, institution_id, and the tuple of account data. Groupings are best processed when sorted by transaction_datetime.
Example six transaction TSV in this format
cb0efb74-5c8c-4388-931d-24bd66514191 01JVAWFJBDH56425F35VQYQ8T0 CITY NATIONAL BANK Basic Checking depository checking 940316 33880117714386590940316 2025-03-22T00:15:49Z 828.61 USD CASH APP*KALISTA HOWELL800-9691940 CAUS US settled 2025-03-22T03:57:11Z 5373 {"txn_uuid":"cb0efb74-5c8c-4388-931d-24bd66514191","account_uuid":"243e88f2-4ba8-45ef-84c1-cdd53bd19307","end_user_uuid":"b09d6d65-168e-4bac-882e-2e10671bc766"}
c456d28b-8f2c-406e-a5dc-d2c2763bde3e 01JVAWFJBDH56425F35VQYQ8T0 CITY NATIONAL BANK Basic Checking depository checking 940316 33880117714386590940316 2025-03-23T13:09:54Z 1206.09 USD CASH APP*CODY ENNIS*ADD800-9691940 CAUS US online settled 2025-03-24T20:25:06Z 5091 {"txn_uuid":"0456d28b-8f2c-406e-a5dc-d2c2763bde3e","account_uuid":"243e88f2-4ba8-45ef-84c1-cdd53bd19307","end_user_uuid":"b09d6d65-168e-4bac-882e-2e10671bc766"}
ac5b680e-56ef-4fb4-86ca-c921d6976031 01JVAWFJBDH56425F35VQYQ8T0 CITY NATIONAL BANK Basic Checking depository checking 940316 33880117714386590940316 2025-03-25T15:45:51Z 1705.10 USD TICKPICK NEW YORK NYUS US online settled 2025-03-28T11:34:56Z {"txn_uuid":"ac5b680e-56ef-4fb4-86ca-c921d6976031","account_uuid":"243e88f2-4ba8-45ef-84c1-cdd53bd19307","end_user_uuid":"b09d6d65-168e-4bac-882e-2e10671bc766"}
a596a06a-2051-4c85-9d4d-2af8bd989e2a 01JVAWMQ79QS7E64JRD6TC5Z33 BMO HARRIS BANK Basic Savings depository savings 2715 2023-01-03T10:32:35Z 1869.60 USD LYFT *TEMP AUTH HOLD +18552800278 CAUS US other debit settled 2023-01-03T21:37:50Z {"txn_uuid":"7596a06a-2051-4c85-9d4d-2af8bd989e2a","account_uuid":"b3c909b9-cdf5-45e2-87ba-3be7a2814a3a","end_user_uuid":"03f9ad37-e83e-452d-b543-51485e9caa34"}
a5355f4b-85a5-4418-85b7-9b7ea80c3f47 01JVAWMQ79QS7E64JRD6TC5Z33 BMO HARRIS BANK Basic Savings depository savings 2715 2023-01-03T12:59:24Z 728.06 USD APPLE.COM/BILL 866-712-7753 CAUS US in store settled 2023-01-04T12:06:42Z 3945 {"txn_uuid":"5a355f4b-85a5-4418-85b7-9b7ea80c3f47","account_uuid":"b3c909b9-cdf5-45e2-87ba-3be7a2814a3a","end_user_uuid":"03f9ad37-e83e-452d-b543-51485e9caa34"}
de012c23-19b0-44c1-a4db-f7ef0fc412f2 01JVAWMQ79QS7E64JRD6TC5Z33 BMO HARRIS BANK Basic Savings depository savings 2715 2023-01-03T20:36:43Z 1033.14 USD CASH APP*JACQUELYN HILL800-9691940 CAUS US other settled 2023-01-04T01:46:18Z {"txn_uuid":"de012c23-19b0-44c1-a4db-f7ef0fc412f2","account_uuid":"b3c909b9-cdf5-45e2-87ba-3be7a2814a3a","end_user_uuid":"03f9ad37-e83e-452d-b543-51485e9caa34"}
Posting transactions to Pinwheel
A single batch can consist of multiple files to upload. We suggest limiting the file size to 100MB. With your Pinwheel API key, POST to https://api.getpinwheel.com/gateway/v1/transactions_batch/files with the following JSON keyed values:
date_range_start– ISO 8601 formatted minimumtransaction_datetimeacross files in the batchdate_range_end– ISO 8601 formatted maximumtransaction_datetimeacross files in the batch.file_count– an integer count of how many files you will upload in this batch. Max 100.
{ "data":{
"date_range_start": "2025-01-01T00:00:00Z",
"date_range_end": "2025-12-31T00:00:00Z",
"file_count": 1 }}
You will get a response including the batch_id, the upload_urls to which you can post the files to, and expires_at time. The URLs will be valid for just under 5 minutes given round-trip time.
> … send … >
POST /gateway/v1/transactions_batch/files
Host: api.getpinwheel.com
Content-Type: application/json
Pinwheel-Version: 2025-07-08
x-api-secret: YOUR-API-SECRET
{
"data": {
"date_range_start": "2025-01-01T00:00:00Z",
"date_range_end": "2025-12-31T00:00:00Z",
"file_count": 2
}
}
< … example response … <
{
"batch_id": "0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed",
"upload_urls": [
"https://….s3.amazonaws.com/upload/batched_transactions/ffffffff-ffff-ffff-ffff-ffffffffffff/2025-01-01_2025-12-31_0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed_002_001.tsv.gz?AWSAccessKeyId=AS…&Signature=KIok…ndEU%3D&content-type=text%2Ftab-separated-values&x-amz-security-token=IQoJb3JpZ2luX……%3D%3D&Expires=1760115099",
"https://….s3.amazonaws.com/upload/batched_transactions/ffffffff-ffff-ffff-ffff-ffffffffffff/2025-01-01_2025-12-31_0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed_002_002.tsv.gz?AWSAccessKeyId=AS…&Signature=fYav…6ig%3D&content-type=text%2Ftab-separated-values&x-amz-security-token=IQoJb3JpZ2luX…%3D%3D&Expires=1760115099"
],
"expires_at": "2025-10-10T16:51:39.081266"
}
You will then post your transaction batch files to the upload_urls provided.
Checking if transactions were ingested
With your Pinwheel API key, use the GET https://api.getpinwheel.com/gateway/v1/transactions_batch/files?batch_id={batch_id} endpoint to check the status of the ingestion.
> … send … >
GET /gateway/v1/transactions_batch/files?batch_id=0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed
Host: api.getpinwheel.com
Content-Type: application/json
Pinwheel-Version: 2025-07-08
x-api-secret: YOUR-API-SECRET
< … example response … <
{
"batch_id": "0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed",
"status": "completed",
"files": [{
"file_number": 1,
"file_status": "ingested",
"s3_presigned_uri": "https://….s3.amazonaws.com/upload/batched_transactions/ffffffff-ffff-ffff-ffff-ffffffffffff/2025-01-01_2025-12-31_0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed_002_001.tsv.gz?AWSAccessKeyId=AS…&Signature=KIok…ndEU%3D&content-type=text%2Ftab-separated-values&x-amz-security-token=IQoJb3JpZ2luX……%3D%3D&Expires=1760115099",
"s3_presigned_expiration": "2025-10-10T16:51:39.081266"
}, {
"file_number": 2,
"file_status": "ingested",
"s3_presigned_uri": "https://….s3.amazonaws.com/upload/batched_transactions/ffffffff-ffff-ffff-ffff-ffffffffffff/2025-01-01_2025-12-31_0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed_002_002.tsv.gz?AWSAccessKeyId=AS…&Signature=fYav…6ig%3D&content-type=text%2Ftab-separated-values&x-amz-security-token=IQoJb3JpZ2luX…%3D%3D&Expires=1760115099",
"s3_presigned_expiration": "2025-10-10T16:51:39.081266"
} ],
"created_at": "2025-10-10T16:41:39.081266",
"started_at": "2025-10-10T16:41:40.819209",
"completed_at": "2025-10-10T16:42:13.101926",
"error_message": null
}
Validation details
In case your files’ status are failed or invalid, you can get a collection of ingestion errors via the GET https://api.getapipinwheel.com/gateway/v1/transactions_batch/files?batch_id={batch_id}&include=errors endpoint.
> … send … >
GET /gateway/v1/transactions_batch/files?batch_id=0199c9e2-a9bf-7c9e-b426-c60a8b4b09ed&include=errors
Host: api.getpinwheel.com
Content-Type: application/json
Pinwheel-Version: 2025-07-08
x-api-secret: YOUR-API-SECRET
< … example response … <
[{
"file_number": 1,
"errors": []
}, {
"file_number": 2,
"errors": [
{"row": 242, "error": "Missing an end_user_id"},
{"row": 837, "error": "Missing a description"},
{"row": 837, "error": "Truncated enriched_trasaction_details"}
]
}]
Updated about 5 hours ago