Skip to content
Sad Subscription Analytics
Menu

CSV import

The Import CSV modal on the Manual Payments page loads many subscriptions and one-off payments at once from a spreadsheet.

Accepted columns

The importer reads ten columns by header name; column order and letter case do not matter, and any other column is ignored.

ColumnRequiredFormatExample
typeYes"subscription" or "one_off"subscription
emailYesA valid email address[email protected]
nameNoFree textJane Doe
planNoFree text, up to 255 charactersGold plan
amountYesA plain number, dot decimal, optional comma thousands separator19.99
currencyNo3-letter code; blank becomes USDUSD
intervalSubscriptions onlymonthly, quarterly, half-yearly, annual, or custommonthly
custom_daysOnly when interval is customA whole number of days, 1 or more45
dateYesYYYY-MM-DD2026-01-15
statusNo (subscriptions only)"active" or "cancelled"; blank means activeactive

Accepted statuses, and a limitation on cancelled rows

A subscription row's status must be exactly "active" or "cancelled" (or blank, which means active); other spellings, including the American "canceled", are rejected. One-off rows are never status-checked.

Duplicate detection

Each row is fingerprinted from its type, email, amount, currency, date and plan or item text. A row matching a record already on file, or an earlier row in the same file, is skipped and counted as a duplicate. Re-importing the same file a second time adds nothing.

Dry run versus import

Preview runs the whole import as a dry run, writing nothing, and reports how many rows would import, how many are duplicates, how many are the template's own example rows, and how many have errors.

Import runs the same classification for real, writes every valid row in one pass, then reports the same four counts for what actually happened.

The template and its example rows

Download CSV template gets you manual-payments-template.csv: the ten column headers plus three filled-in example rows, one recurring subscription, one with a custom billing interval, and one one-off payment.

Any row that is byte-for-byte identical to an example row is ignored, so you can leave them in place while you add real rows above or below them.

Row error messages

MessageWhen it appears
Type must be "subscription" or "one_off".The type column is missing or not one of those two words.
A valid email is required.The email column is blank or not a real email address.
Amount must be a plain number such as 19.99 or 1,999.00 (use a dot for the decimal point).The amount uses a comma decimal, a currency symbol, or a sign.
Amount must be greater than zero.The amount is blank, zero, or negative.
Date must be in YYYY-MM-DD format.The date is missing or not a real calendar date in that format.
Interval must be monthly, quarterly, half-yearly, annual, or custom.A subscription row's interval column does not match one of those words.
Custom interval requires custom_days of 1 or more.Interval is "custom" but custom_days is blank or less than 1.
Status must be "active" or "cancelled", or left blank for active.A subscription row's status column holds anything else.
Could not import this row.The row passed every check but failed to save.

Up to 20 row errors are listed by row number; beyond that, a final line states how many more there were.