File upload imports sales transactions from a CSV, which is the fastest way to bring in history when you are onboarding or backfilling a period. Uploaded transactions behave exactly like ones created through the API: they count toward nexus, feed compliance reporting, and land on the right return at filing time.
This page is the column reference. Download the template from the app, or with Download a CSV import template, then use the tables below to fill it in.
CSV column names are snake_case, such as transaction_external_id. They describe the file, not the API, so they do not change to camelCase on the Tenanted API. The camelCase names appear only in API responses, such as the resultData rows that Validate a CSV file returns.
Before You Start
Five columns have to be in the fileA file missing any of these is rejected before any row is read. The first four need a value on every row. amount is required as a column so that a file without it cannot book every line at 0.00. Every other column can be left out entirely, and the value falls back to the default listed on its row. Leaving one out is not the same as including it and leaving cells blank, which the next two cards cover.
Omitting a column is safe. Blanking a cell is notA default only applies when the column is absent from the file. Once you include a column, the upload validator expects a value in it, and it is stricter than the defaults suggest.
Column absentThe documented default applies to every row. This is the safe way to skip a column you have no data for.
Column present, cell emptyRejected for operation, and a conflict on any transaction-level column where another row of the same transaction does carry a value. Delete the column, or fill it in on every row.
One transaction, many rows, identical valuesA multi-line sale is several rows sharing one transaction_external_id. Only the line item columns are allowed to differ between them. Every transaction-level column you include has to carry the same value on every row, including the addresses, the buyer, the date, status, currency, transaction_type and operation.Filling a column in on the first row and leaving it blank on the rest is the most common way to trip this, because an empty cell counts as a different value. The error names the column and the row it first saw: Inconsistent 'transaction_type': '' vs 'SALE'.
Save the file as text, not as a spreadsheetFormat every cell as General before exporting. A cell typed as Date or Number is re-serialized on save, which is the most common reason a file that looks correct fails validation. Postal codes are the other one: a leading zero has to survive the export.
Reading the badges
RequiredA value in every row.
ConditionalRequired in a stated case. The rule is on the row.
OptionalLeave the column out and the default applies.
The downloadable template carries two columns the importer does not read. total_amount is ignored because transaction totals come from the line items, so fill in amount and tax_amount per row instead. source is ignored too: each transaction takes its source from the import itself.
On the Tenanted API, an import's source is the source you send to the Imports endpoints, and it reads back as transactionSource.
Transaction Columns
These describe the sale. Every row of a multi-line sale repeats them, and rows sharing a transaction_external_id are read as line items of one transaction.
Identity, type and date9 columns
transaction_external_idRequired
Your own id for the transaction. Kintsugi keys the record on it.
INVALID and ARCHIVED are set by Kintsugi and rejected on import. Transaction-level, so repeat it on every row of a multi-line sale.
operationConditional
What the importer should do with the row.
e.g.IMPORT
Defaults to IMPORT when the column is absent
Accepted values
IMPORTUPDATEARCHIVE
Required as soon as the column is in the file. A blank cell is rejected outright rather than falling back to IMPORT, so leave the column out or fill it in on every row.
dateRequired
When the transaction took place.
e.g.2024-03-23T00:00:00
The template uses YYYY-MM-DDT00:00:00
Format the cell as General. A cell typed as Date is rewritten on save and the row fails.
currencyOptional
Currency for every amount on the row.
e.g.USD
Any ISO 4217 codeDefaults to USD
Transaction-level, so repeat it on every row of a multi-line sale.
descriptionOptional
Free text describing the transaction.
e.g.Heirloom Ring Size 8.5 14K
Max 1000 characters
marketplaceOptional
Whether a marketplace facilitator collected the tax on this sale.
e.g.FALSE
TRUE, FALSE, true, false, 1, 0, t, fDefaults to FALSE
The buyer5 columns
customer_idRequired
Your own id for the buyer. Exemptions attach to it.
Transaction-level, so every row of a multi-line sale needs the same buyer.
customer_nameOptional
The buyer's full name.
e.g.Faith Ortega
Max 200 characters
customer_emailOptional
The buyer's email address.
e.g.michele45@example.com
Any valid email addressMax 200 characters
customer_company_nameOptional
The buyer's registered or legal business name.
e.g.Example Company Inc.
Max 200 characters
tax_idOptional
The buyer's registration number.
Max 100 characters
Transaction-level, so every row of a multi-line sale needs the same value.
Address Columns
Addresses decide the rate, so this is the part of the file worth checking twice. A postal code resolves to a local jurisdiction; a state on its own does not.
The one ruleEvery row needs a postal code and a country from one side or the other. Which side is up to you, but you cannot leave both empty.
Eithership_to_postal_code + ship_to_country
Orbill_to_postal_code + bill_to_country
Ship to7 columns
ship_to_countryConditional
The recipient's country.
e.g.US
Max 100 characters
Required unless bill_to_postal_code and bill_to_country are both present. A country of PR is read as state PR in the US.
ship_to_postal_codeConditional
The recipient's postal code. This is what resolves the local rate.
e.g.21830
US: 5-digit ZIP or ZIP+4Max 50 characters
Checked when the country is US or blank. A 4-digit code is accepted and gets its leading zero back, but exporting the column as text is safer.
ship_to_stateOptional
The recipient's state or province.
e.g.CO
US: 2-letter code or full state nameCA: 2-letter province code
ship_to_cityOptional
The recipient's city.
e.g.Aurora
ship_to_street_line_1Optional
First line of the street address.
e.g.811 Eric Flat Suite 183
Max 1000 characters
ship_to_street_line_2Optional
Second line of the street address.
e.g.Apt 1606
Max 1000 characters
ship_to_phoneOptional
The recipient's phone number.
e.g.+1 665-869-8307
Max 50 characters
Bill to7 columns
bill_to_countryConditional
The billed party's country.
e.g.US
Max 100 characters
Required unless ship_to_postal_code and ship_to_country are both present. A country of PR is read as state PR in the US.
bill_to_postal_codeConditional
The billed party's postal code. This is what resolves the local rate.
e.g.21830
US: 5-digit ZIP or ZIP+4Max 50 characters
Checked when the country is US or blank. A 4-digit code is accepted and gets its leading zero back, but exporting the column as text is safer.
bill_to_stateOptional
The billed party's state or province.
e.g.CO
US: 2-letter code or full state nameCA: 2-letter province code
bill_to_cityOptional
The billed party's city.
e.g.Aurora
bill_to_street_line_1Optional
First line of the street address.
e.g.811 Eric Flat Suite 183
Max 1000 characters
bill_to_street_line_2Optional
Second line of the street address.
e.g.Apt 1606
Max 1000 characters
bill_to_phoneOptional
The billed party's phone number.
e.g.+1 665-869-8307
Max 50 characters
Line Item Columns
One row per line item. The product columns drive classification, and the money columns are what your filings reconcile against.
What this line came to, after discounts and excluding tax.
e.g.550.51
Commas allowed, no currency symbolsA blank cell is 0.00
The column has to be in the file. Transaction totals are summed from these, so this is the figure that has to reconcile.
tax_amountOptional
Tax collected on this line.
e.g.1.69
Defaults to 0.00
quantityOptional
How many units this line covers.
e.g.14
A value below 1 is rejectedDefaults to 1
discount_amountOptional
Discount applied to this line.
e.g.0.69
Defaults to 0.00
When present it has to be greater than 0 and no more than amount. Since amount is already net of discounts, this column records the discount rather than applying it.
exemptOptional
Whether this line is exempt from tax.
e.g.FALSE
TRUE, FALSE, true, false, 1, 0, t, fDefaults to FALSE
customer_exemptOptional
Whether the buyer holds an exemption covering this sale.
e.g.FALSE
TRUE, FALSE, true, false, 1, 0, t, f
Cannot be TRUE when exempt is also TRUE: the row has to say whether the product or the buyer is the reason.
product_name and product_description are optional columns that do real work: Kintsugi classifies the product from them, and classification is what decides taxability per state. A file with bare SKUs imports cleanly and prices badly.
Importing a Refund
A refund is a credit note row that points at the original sale.
1
Mark the row as a credit note
Set transaction_type to FULL_CREDIT_NOTE or PARTIAL_CREDIT_NOTE. That is what makes the next column required.
2
Point it at the original
Put the original sale's id in related_external_id, and give the credit note its own transaction_external_id. A row cannot reference itself.
PCR_Credit note ids are conventionally the original id behind a PCR_ prefix, which keeps the pair legible in an export.
Set the original's status
PARTIALLY_REFUNDED and FULLY_REFUNDED are both accepted values of status, for the original sale's rows.
Kintsugi validates the file before importing any of it, and errors come back per row and per column, so a rejected file tells you which cell to fix rather than just failing.
The same checks are available through the Tenanted API's Imports endpoints:
Validate a CSV file checks a file's contents before you upload it. It always answers 200; read isValid and errors to see whether the file passed.
Get an import by id reports an import's status and its row counts, including validRowCount and invalidRowCount.
Import the validated rows imports the rows that passed. submitModeALL requires every row to have passed validation; VALID_ONLY imports the rows that passed and skips the rest.