# tapcsv — CSV format Give this file to whoever (or whatever agent) builds the recipient list. One row per person. UTF-8, header row, comma-separated (semicolon or tab also accepted). Max 5 MB. ## Columns | column | required | notes | |-----------------|----------|-------| | `id` | strongly recommended | A stable identifier (voter id, CRM id). Re-uploads merge on it, so progress is kept. If missing, tapcsv generates `r00001`… and the export carries them. | | `first_name` | recommended | Used as `{first_name}` in messages. | | `last_name` | recommended | `{last_name}`. | | `phone` | one of phone/email | Any North American format. Normalised to `+1XXXXXXXXXX`. Rows with an unparseable number are kept and flagged "bad phone", not dropped. | | `email` | one of phone/email | Rows with both get both a Text and an Email button. | | `message` | optional | Per-row text message. Overrides the campaign template for that row only. | | `email_message` | optional | Per-row email body. Same rule. | | *anything else* | optional | Kept untouched, returned on export, and usable as a merge field: a column `topic` is `{topic}` in the template. | Header names are forgiving: `First Name`, `firstname`, `FIRST` all become `first_name`; `mobile`, `cell` become `phone`; `voter_id` becomes `id`. ## Merge fields Campaign templates (set in edit mode) use `{column_name}`. Built-ins: `{first_name}`, `{last_name}`, `{phone}`, `{email}`, `{id}`, `{sender}` (the sender name set on the campaign). Unknown fields are left as literal `{braces}` and shown as a QA warning, never silently blanked. ## Keep text messages short and plain Any character outside the GSM-7 set (curly quotes, em dashes, emoji) switches the whole message to 70-character segments instead of 160. Edit mode warns. Use straight quotes and hyphens. ## Minimal example ```csv id,first_name,last_name,phone,email,topic 06001-1234567,Maria,Lopez,(510) 555-0101,maria@example.com,walked Saturday 06001-7654321,Dev,Patel,510-555-0102,,phone bank ``` With the template `Hi {first_name}, it's {sender}. Thank you for your help with {topic} this weekend!` ## Per-row messages example ```csv id,first_name,last_name,phone,message a1,Maria,Lopez,5105550101,"Maria - thank you for Saturday. Scott" a2,Dev,Patel,5105550102,"Dev, thanks for the phone bank. Scott" ``` ## What comes back on export Your columns, in your order, plus: `tapcsv_status` (`not_started`, `sent`, `replied`, `wrong_number`, `opt_out`, `skip`), `tapcsv_channel` (`sms` or `email`), `tapcsv_note`, `tapcsv_tapped_at` (UTC), `tapcsv_tapper`. ## Instructions for an agent building the list 1. Pull the people from the source system. One row per person; de-duplicate on the id. 2. Put the source's stable id in `id`. Never invent one if the source has one. 3. Put the best mobile number in `phone`. Leave it blank rather than guessing; the person then gets an Email button only. 4. Do not include anyone with a do-not-contact flag. 5. If each person needs a different message, fill `message`; otherwise leave it out and let the template do the work. 6. Add any column you want in the message as its own column (`topic`, `shift_date`, `precinct`). 7. Save as UTF-8 CSV and hand it to the operator, who uploads it in edit mode.