- Knowledge Base
- Cross-connector tasks
Prepare a CSV for offline conversions
Offline conversions reach your advertising platforms as a CSV file that Signals reads from a location you control. This article covers what the file has to contain, how each value has to be formatted, and what to check before you send the first one.
One file serves nine destinations: Meta, Google, Snapchat, TikTok, LinkedIn, X, Reddit, Pinterest and OpenAI. Which columns each one requires differs, and that is covered below. Google Store Sales is the exception and uses its own template.
Before you start
Section titled “Before you start”- A source instance in Studio with Offline Events selected as the Integration Type, and a delivery location connected. Signals reads from AWS S3, Google Cloud Storage, FTP or SFTP.
- The sample file for Offline Events, which carries the exact column names and two example rows. Build your export against that rather than against a list you have typed out.
- Agreement on what your conversion event is called, since that name is what the destination reports against.
- A way to apply SHA-256 hashing in your export process, since personal data has to be hashed before it leaves your systems. Apply SHA-256 hashing to PII has snippets for Google Sheets, SQL and the common languages.
Naming the file
Section titled “Naming the file”- Use a consistent pattern that carries the date on which the file is uploaded, for example
sample_offline_conversion_2026_08_06.csv. - If you send more than one file for the same date, add a numeric suffix to the second and later files.
- Keep the naming identical every time. An inconsistent name is the most common reason a file sits in the folder and nothing happens.
Required columns
Section titled “Required columns”Three columns are required by every destination, and a further five are required by some of them or on some rows.
| Column | What goes in it |
|---|---|
| event_time | The time of the conversion, as a UNIX timestamp. Use the moment the purchase happened, not the moment the record was created. |
| event_name | The name of the action, for example Purchase. This is what the destination reports against. |
| last_modified_date | When the record last changed, as a date and time. |
| email_1_hashed | Hashed email address. Required by every destination except OpenAI. |
| One phone column | At least one of the six phone columns. Required by Meta, Snapchat and TikTok. |
| currency | Three-letter ISO currency code. Required on Purchase rows only. |
| value | The amount, as a decimal number. Required on Purchase rows only. |
| action_source | Where the conversion happened. Required by Meta, Snapchat and TikTok. One of phone_call, chat, physical_store, system_generated or other. |
Describing the conversion
Section titled “Describing the conversion”These are optional. Send the ones your destination reports on.
| Column | Notes |
|---|---|
| product_type | product or product_group. |
| product_name, product_category, product_ids, product_brand, product_price, product_quantity | The product side of the conversion. product_ids takes an array. |
| product_group_id | Identifier for a group of product variants. |
| transaction_id | Your reference for the order. This is what stops the same purchase being counted twice if a file is read again. |
| number_items, item_number | The item count, and a unique reference to distinguish events inside one order. |
| delivery_method | in_store, curbside or delivery. |
| customer_status | new, returning or reactivated. |
| predicted_ltv | Predicted lifetime value for the conversion. |
| subscription_id, shop_id, store_code, store_page_id, brand_page_id | Subscription and store references, where the destination uses them. |
| custom_parameter_1, custom_value_1 | One custom key and value. Meta passes them through as sent and Google accepts one. Snapchat, TikTok and X do not accept custom parameters. |
Identifier columns
Section titled “Identifier columns”A row with no identifier cannot be matched to anyone, so this is where match rates are won. Send as many as your transaction record holds.
| Column | Notes |
|---|---|
| email_1_hashed, email_2_hashed, email_3_hashed | Up to three addresses, one per column. The first is required, the others optional. |
| phone_1 to phone_3, in format_a and format_b | Six columns. Send E.164 in the format_b column. |
| first_name_hashed, last_name_hashed | Recommended by Meta and Snapchat. |
| city_hashed, state_hashed, country_hashed, zipcode_hashed | Hashed location. Recommended by Meta and Snapchat. |
| state, zipcode, country | The same location fields in plain text, for the destinations that want them unhashed. |
| gender_hashed, date of birth fields | Four date of birth columns: year, month, day and a single combined column. |
| external_id | Your own identifier for the person, such as a loyalty number or an external cookie ID. Sent in plain text. |
| fbclid | The Meta click ID for the conversion. |
| fbc | The Meta browser cookie value, in the form fb followed by the subdomain index, the creation time and the click ID. |
| fb_login_id | The numeric Facebook user ID, where you have it. |
| gclid, gbraid, wbraid | Google click identifiers, all optional. Set at most one of the three on a row. |
| snapchat_click_id | Snapchat click identifier. |
| client_user_agent, client_ip_address | Sent in plain text. Used by Google, Snapchat, Reddit and Pinterest. |
| first_name, last_name, title, company_name | Plain text, used by LinkedIn. |
How each value has to be formatted
Section titled “How each value has to be formatted”Normalize the value first, then hash it. A hash only matches if both sides normalized the value the same way, so formatting is not cosmetic here.
| Field | Format |
|---|---|
| event_time | UNIX timestamp. |
| Trim leading and trailing whitespace and convert everything to lowercase. One address per cell. | |
| Phone | E.164. A leading plus sign, then the country code and the number, up to fifteen digits, with whitespace, brackets and hyphens removed. For example +919876543210. Put this in the format_b column. |
| Names and city | Letters a to z only. Trim whitespace, lowercase, no punctuation or special characters. |
| State, hashed | For the United States, the two-character code in lowercase. Elsewhere, the full region name in lowercase with no punctuation, special characters or whitespace. |
| State, plain | For the United States, the two-letter code. Elsewhere, the full region name. |
| Postcode | Lowercase, no whitespace. United States, first five digits only. United Kingdom, the Area, District and Sector format. |
| Country | Two-letter ISO 3166-1 alpha-2 code. Lowercase in the hashed column. |
| Gender | m for male, f for female. |
| Date of birth | DD/MM/YYYY in the combined column, or YYYY, MM and DD in the three separate columns. |
Hashing
Section titled “Hashing”- Every column whose name ends in
_hashedcarries the SHA-256 hash of the normalized value, not the value itself. - Everything else stays in plain text. That includes the whole event side of the file, external_id, the plain state, zipcode and country columns, the click identifiers, the user agent and the IP address.
- Never hash an empty cell. Hashing a blank produces a valid-looking 64-character value that matches nothing, and it is a common export bug. Leave the cell empty instead.
What differs by destination
Section titled “What differs by destination”- A hashed email address is required by every destination except OpenAI.
- Meta, Snapchat and TikTok require a phone number and an action source. The others do not.
- Meta and Snapchat recommend the name and location fields. Pinterest accepts them as optional. Google, TikTok, X, LinkedIn and Reddit largely do not use them.
- Click identifiers are per platform: fbclid and fbc for Meta, gclid, gbraid or wbraid for Google and all three optional, snapchat_click_id for Snapchat.
- LinkedIn is the only destination that uses the plain first name, last name, title and company name columns.
- Custom parameters go through to Meta as sent, Google accepts one, and Snapchat, TikTok and X accept none.
Google Store Sales uses a different file
Section titled “Google Store Sales uses a different file”Store Sales has its own 22-column template rather than extra columns on this one. It keeps the same event and identifier basics, adds language_code, third_party_user_id and a custom key and value pair, and takes the location fields in plain text rather than hashed. Use the Store Sales sample file, not this one.
The file itself
Section titled “The file itself”- A comma-separated
.csvfile. - A header row naming the columns. You only need the columns you are actually sending, so a file with fewer columns than the template is fine.
- There is no limit on the number of rows or on the size of the file.
A header mismatch fails quietly. A column whose header does not match the template is skipped rather than reported, and the file processes normally without it, so a renamed or misspelled header does not produce an error. It produces events with that field missing. If a match rate is lower than you expected on a file that processed cleanly, compare the header row against the template first, including case and underscores.
Attribution windows
Section titled “Attribution windows”Rows older than the destination’s attribution window are dropped without an error, so filter them out in the export rather than sending them and wondering where they went.
- Meta accepts conversions up to 90 days old.
- Google Store Sales accepts conversions up to 60 days old.
How often to send
Section titled “How often to send”Daily is the recommendation. Three files a week is the practical minimum. Sending less often than that gives the destination less to learn from, and shows up as weaker optimization rather than as an error.
Check before you send
Section titled “Check before you send”- Column names match the sample file exactly, including case and underscores.
- The file name carries today’s date, and a second file for the same date has a suffix.
- event_time is a UNIX value and last_modified_date is populated.
- Purchase rows carry both currency and value.
- Phone numbers are in E.164, in the format_b column, and carry a country code.
- Every
_hashedcolumn contains a hash, and no blank cell has been hashed. - No row is older than the destination’s attribution window.
- The same date has not already been sent in an earlier file.

