Raw event export files
Every folder, file and column of the raw event export to your own S3, Google Cloud Storage or SFTP storage, plus the daily lookup files and empty-hour files.
The raw event export delivers the ad server’s event logs to your own storage, hour by hour. This page lists what lands there, so a data team can load the files without asking what a folder or a column means.
Who this is for
Whoever loads the files into a warehouse or query tool. There is no console page for the export: Epom support sets it up, and you need access to the storage, not a permission in the console.
Folders and file names
<root>/stream=<name>-<id>/t=<event>/dt=<YYYY-MM-DD>/h=<H>/<YYYY-MM-DD-HH>-<server>.parquet.gz
| Part | Meaning |
|---|---|
<root> | The folder agreed when the export was set up |
stream= | The name of this export, fixed for your network |
t= | The event type, see Event types |
dt=, h= | Day and hour in UTC, whatever time zone your reports use. h= has no leading zero: h=3 |
<server> | The number of the ad server that wrote the file |
stream=, t=, dt= and h= are standard Hive-style partition folders, which query engines can
read as columns. There is one file per ad server, per hour, per event type, so to read an hour
read the whole h= folder.
Files are ordinary Parquet with gzip compression inside, despite the .gz at the end of the name.
Treat them as Parquet: a tool that guesses the format from the extension may need telling.
Event types
t= | What one row is |
|---|---|
adRequests | An ad request |
adRequestsRepeated | A repeated ad request, the one counted as Repeated in reports |
noImpressions | A request answered without an ad |
impressions | An impression |
passback | A passback |
clicks | A click |
actions | A conversion, or a VAST playback event tracked as a system action |
actionsExpired | A conversion that matched an event older than its attribution window |
piggyback | A piggyback pixel the ad server fired, with its own columns |
A network can have some of these turned off, in which case that folder never appears.
Columns
Every event type except piggyback has these columns. Columns are only ever added at the end, so
files from before a column existed lack it: read them with schema merging on.
Time and identity
| Column | Type | Meaning |
|---|---|---|
date | long | Event time, in milliseconds since the Unix epoch. It has no time zone problem, so use it for time, not the folder |
transactionId | long | Ties together one ad serve: its request, impression, clicks and conversions |
epomUUID | string | User ID. Empty where GDPR keeps it off the record |
ifa | string | Advertising ID, under the same GDPR rule |
duration, eventType | long, int | Not used: always 0 and 1. The t= folder is the event type |
Entities
The rows carry IDs, not names. See Daily lookup files.
| Column | Type | Meaning |
|---|---|---|
placementId, zoneId, siteId | long | The inventory. 0 when the request named no known placement |
bannerId, campaignId, advertiserId | long | The demand. 0 when no creative is involved |
actionId | long | The conversion action or VAST event. 0 outside actions and actionsExpired |
channel | string | The channel |
placementType | long | Numeric ID of the placement type |
placementWidth, placementHeight | long | Placement size |
placementLabels, bannerLabels | array of long | Label IDs |
Money
| Column | Type | Meaning |
|---|---|---|
gross | double | What this event charged the advertiser, in the network currency |
net | double | What this event earned the publisher, in the network currency |
Sum the column to get a total. Neither includes a share of the ad server fee, as in the log reports.
Where the viewer is
| Column | Type | Meaning |
|---|---|---|
ip | string | Viewer IP, masked where GDPR requires it |
remoteIp, xforwardedFor | string | The connecting address and the forwarding chain, masked the same way |
country | string | Two-letter code, - when unknown. Great Britain is UK, not GB |
location | string | region - city |
carrier, connectionType, proxy | string | Mobile carrier, connection type, proxy type |
timeZone | float | The browser’s UTC offset in hours |
timeZoneName, browserCountry | string | The browser’s IANA zone, and the country that zone points to |
language, acceptLanguage | string | Browser language and the Accept-Language header |
timeZoneName and language are sent with the ad request, so they are often empty on impressions
and clicks.
Device
| Column | Type | Meaning |
|---|---|---|
userAgent | string | User agent, replaced with a placeholder where GDPR requires it |
browser, browserVersion, os, osVersion | string | browserVersion is the major version only |
deviceFormat, deviceVendor, deviceModel, deviceModelCode | string | Device as detected |
deviceReleaseYear, deviceRAM | int | 0 or empty when unknown |
priceBand | string | Device price band |
browserRank, hardwareRank, platformRank | int | Device-quality rankings |
screenWidth, screenHeight | int | Screen size |
isWebApp, isIncognitoMode | boolean | Empty when unknown |
Where the request came from
| Column | Type | Meaning |
|---|---|---|
origin | string | Top Domain: the page the ad was served on |
referer | string | The domain the event’s own request came from, which can be a player or an ad frame |
hdrOrigin, hdrReferer, hdrXReferer | string | The headers that event arrived with |
appPackage | string | App package name |
requestUrl | string | The full request URL. Empty unless Epom support has turned it on for your network |
customParameters, cookies | map | Custom parameters and cookies of the request, minus any your network excludes from logging |
trafficSource | string | HEADER_BIDDING for traffic answered by the header-bidding endpoint, otherwise empty |
Quality and outcome
| Column | Type | Meaning |
|---|---|---|
isFraud | boolean | The ad server flagged this event as fraud |
invalid | boolean | The hit failed validation and did not count. These rows exist only where your network keeps invalid records |
rejectReason | string | Why tracking validation flagged the hit. A check in monitoring mode flags a hit that still counts |
riskScore, riskLevel, riskCategory | double, string | Traffic-quality scoring |
responseCode | int | HTTP status returned to this hit |
responseType | int | On ad requests: 0 ad markup, 1 default code, 2 empty, 3 VPAID. 4 to 6 are internal variants |
attributionType | int | On conversions: 0 unspecified, 1 post-click, 2 post-view, 3 post-click expired, 4 post-view expired, 5 system |
attributedEventDate | long | On conversions: the time of the click or impression it was attributed to, in epoch milliseconds |
fraudMetric, rejectedByForensiq and piggybacks are in the schema but always empty.
Piggyback columns
t=piggyback has its own short schema: date, transactionId, placementId, bannerId,
actionId, country, channel, url (the pixel fired) and responseCode (what it answered).
Daily lookup files
Epom support can also switch on daily ID-to-name files, the equivalent of match tables in Google Ad
Manager Data Transfer. They sit under their own top-level folder, so nothing reading the stream=
folders meets a different schema:
<root>/match=<name>-<id>/t=<table>/dt=<YYYY-MM-DD>/<table>-<YYYY-MM-DD>.parquet.gz
t= | Columns | Join on |
|---|---|---|
banners | bannerId, bannerName, universalAdId (the ISCI or Ad-ID code), universalAdIdRegistry, campaignId, campaignName, advertiserId, advertiserName, deleted | bannerId |
placements | placementId, placementName, zoneId, zoneName, siteId, siteName, deleted | placementId |
actions | actionId, actionName, vastEvent (the event name the player fires, such as firstQuartile, empty for an ordinary conversion), deleted | actionId |
Each file is a snapshot of the whole network taken once a day, shortly after midnight UTC. Deleted entities stay in because older events still point at them.
Corner case: a creative created after today’s file was written appears in tomorrow’s. For a row of today that finds no name, look in the next day’s file.
Empty-hour files
Epom support can also switch on a file for an hour with nothing to report. Each ad server then writes, for every event type it had no events of in the hour that just closed, a valid Parquet file with no rows:
.../h=14/2026-11-08-14-5.empty.parquet.gz
With this on, an hour reads like this:
In the h= folder for a server | Meaning |
|---|---|
A .parquet.gz file | Events, normally |
A .empty.parquet.gz file | That server was up for the whole hour and had no events of this type |
| Neither | A problem: the server was down for some or all of the hour, or could not deliver. Ask Epom support |
Reading the folder as usual is safe, because an empty file adds no rows.
Corner case: a late real file arrives under its normal name beside the empty one. Nothing is overwritten, and reading both gives the right rows. If a server stalled and wrote that hour’s events later, the hour can show an empty file while its events sit in a later file:
datestill tells them apart.
Corner cases & gotchas
Corner case: an event can land in the file of the next hour, because a file holds what the server wrote to disk during that hour, not what happened during it. If the server stalled, the gap can be longer. Use
datefor time and the folders only to find files.
Corner case: folder days are UTC and report days are your report’s zone, so a day’s files never line up exactly with a report’s day. Filter on
dateto compare.
Corner case: a row holds no ISCI or Ad-ID code even though the log reports show one. Reports look it up from the creative when you run them. Join the
bannerslookup file to get it.