How to Upload Offline Conversions to Facebook (Meta) Ads
Uploading offline conversions to Meta (Facebook/Instagram) requires the Conversions API (CAPI) — Meta’s server-side event tracking endpoint. Unlike Google Ads, which accepts CSV files, Meta requires all offline events to be sent via API.
This guide from The Upload Offline Conversions App (TUOCA) walks through every step, from setup to your first successful event submission.
Prerequisites
- ✅ Meta Business account
- ✅ Facebook/Instagram ad campaigns running
- ✅ Pixel created (required for CAPI access)
- ✅ Access token generated
- ✅ Conversion data (leads with email/phone and conversion details)
Step 1: Create a Pixel (If You Don’t Have One)
- Go to Meta Events Manager (business.facebook.com/events_manager)
- Click Connect Data Sources → Web
- Name your pixel
- Click Create Pixel
- Note the Pixel ID (you’ll need this for API calls)
Step 2: Generate an Access Token
- In Events Manager, select your pixel
- Click Settings
- Scroll to Conversions API
- Click Generate Access Token
- Copy and securely store the token
Important: This token doesn’t expire automatically, but it’s tied to your user account. If your account permissions change, you may need to regenerate it.
Step 3: Prepare Your Data
Required Fields
| Field | API Name | Format | Required |
|---|---|---|---|
| Event name | event_name | String (e.g., “Purchase”) | ✅ Yes |
| Event time | event_time | Unix timestamp | ✅ Yes |
| Action source | action_source | system_generated (for offline) | ✅ Yes |
| Hashed email | user_data.em | SHA-256 of lowercase, trimmed email | ✅ At least one |
| Hashed phone | user_data.ph | SHA-256 of E.164 phone | Recommended |
Recommended Fields
| Field | API Name | Format | Purpose |
|---|---|---|---|
| fbc | user_data.fbc | fb.1.{timestamp}.{fbclid} | Click-level matching |
| Event ID | event_id | Unique string | Deduplication |
| Value | custom_data.value | Number | Revenue tracking |
| Currency | custom_data.currency | ISO 4217 | Currency code |
Step 4: Normalize and Hash PII
Critical: PII must be normalized THEN hashed with SHA-256. The order matters.
Raw: " John.Doe@Gmail.COM "
Step 1: "john.doe@gmail.com" (lowercase, trim)
Step 2: SHA-256 hash → "e8b6c3..." (hash)
Phone
Raw: "(555) 123-4567"
Step 1: "+15551234567" (E.164 format)
Step 2: SHA-256 hash → "d4f5c3..." (hash)
Use the SHA-256 Hasher and E.164 Formatter tools to verify.
Step 5: Format the API Request
Endpoint
POST https://graph.facebook.com/v26.0/{PIXEL_ID}/events
Request Body
{
"data": [{
"event_name": "Purchase",
"event_time": 1714502400,
"event_id": "job-2026-04-001",
"action_source": "system_generated",
"user_data": {
"em": ["e8b6c3a4f2d1..."],
"ph": ["d4f5c3b1e72a..."],
"fbc": "fb.1.1714000000.IwAR3x7..."
},
"custom_data": {
"value": 4500.00,
"currency": "USD"
}
}],
"access_token": "YOUR_ACCESS_TOKEN"
}
Key Notes
emandphare arrays — you can send multiple emails/phones per eventevent_timeis a Unix timestamp (seconds, not milliseconds)action_sourcemust besystem_generatedfor offline CRM eventsfbcis the processed FBCLID (format:fb.1.{timestamp}.{fbclid})
Step 6: Send the Event
Using cURL (Testing)
curl -X POST \
"https://graph.facebook.com/v26.0/YOUR_PIXEL_ID/events" \
-H "Content-Type: application/json" \
-d '{
"data": [{
"event_name": "Purchase",
"event_time": 1714502400,
"event_id": "test-001",
"action_source": "system_generated",
"user_data": {
"em": ["YOUR_HASHED_EMAIL"]
},
"custom_data": {
"value": 100.00,
"currency": "USD"
}
}],
"access_token": "YOUR_TOKEN"
}'
Successful Response
{
"events_received": 1,
"messages": [],
"fbtrace_id": "ABC123..."
}
Step 7: Verify in Events Manager
- Go to Events Manager → your pixel
- Click Test Events tab
- Check that your test event appears
- Click the Overview tab to see event counts
- Check Event Match Quality — aim for 6+ (out of 10)
The 7-Day Window Challenge
Meta’s event window is only 7 days. For service businesses with longer sales cycles, you need a strategy:
Strategy: Multi-Stage Events
Day 0: Ad click (FBCLID captured)
Day 1: Phone call → Send "Contact" event ✅ (within 7 days)
Day 3: Estimate scheduled → Send "Lead" event ✅ (within 7 days)
Day 14: Job booked → Send "Purchase" ❌ (outside 7 days for FBCLID)
BUT: Send with hashed email/phone → PII matching still works
Upload the high-funnel event (“Contact”) within 7 days to establish the click-level match. Then send the final revenue event with hashed PII for people-based matching.
Common Errors
| Error | Cause | Fix |
|---|---|---|
| Invalid hash format | PII not properly SHA-256 hashed | Use 64-character hex lowercase |
| Missing user_data | No matching identifiers | Include at least em or ph |
| Invalid event_time | Timestamp too old or future | Must be within 7 days, not in the future |
| Duplicate event | Same event_id sent twice | Expected — Meta silently deduplicates |
| Permission denied | Token expired or lacks scope | Regenerate access token |
How TUOCA Automates Meta Uploads
TUOCA handles the entire CAPI pipeline:
- Data extraction — Pulls from CallRail, CRM, or webhooks
- PII normalization — Lowercases email, formats phone to E.164
- SHA-256 hashing — Hashes all PII per Meta’s requirements
- fbc construction — Converts raw FBCLIDs to fbc format
- Event formation — Builds the CAPI JSON payload
- Batch upload — Sends events to the Conversions API
- Deduplication — Generates consistent event_ids
- Error handling — Logs and categorizes API errors
- EMQ monitoring — Tracks Event Match Quality scores
Next Steps
- ✅ Create pixel and generate access token
- ✅ Send a test event via cURL
- ✅ Verify in Events Manager → Test Events
- ✅ Set up TUOCA for automated CAPI uploads
- ✅ Monitor Event Match Quality — aim for 6+