Reading the columns
How to navigate 342 columns without reading 342 rows of documentation.
Column families
Every column belongs to a family identified by its prefix. Here is the whole ticket row, by family:
| Family | Columns | What it describes |
|---|---|---|
TicketOwnerInfo* | 107 | Your own custom registration fields — see below |
Ticket* | 41 | The ticket itself: number, status, validity, prices, wallet links |
TicketOwner* | 40 | The person who attends: name, address, contact, company, function |
Buyer* | 33 | The person who paid |
Sale* | 16 | The purchase transaction, including SaleUtm* campaign parameters |
Fairevent* | 16 | The event edition: name, year, dates, numbers |
Legitimation* | 12 | Proof presented for a restricted article, and its validity window |
UpsellingSource* / Upselling* | 10 | Upgrade origin and value uplift |
CrossSellingSource* / CrossSelling* | 10 | Cross-sell origin and value uplift |
PromotionCode* | 8 | Discount or access code applied |
Article* | 8 | What was sold, including the badge category |
Exhibitor* | 6 | Which exhibitor issued the ticket, where applicable |
FairBrand* | 4 | The event series the edition belongs to |
Currency* | 4 | Currency of the monetary columns |
Payment* | 4 | How it was paid |
Mandator*, Cart*, Third* | 3 | Tenant, cart reference, third-party registration state |
TicketUsages follows the same convention with a Usage-flavoured core (Timestamp, Entrance*, Location*, IsEntry, IsExit, IsFirstOf*) plus a repeated slice of ticket, article and event columns so common questions need no join.
The custom registration fields
Almost a third of the ticket row — TicketOwnerInfo2 … TicketOwnerInfo100 and TicketOwnerInfoLookup1 … TicketOwnerInfoLookup8 — is not a fixed schema. These are the free-form fields your own registration forms write into.
| What they are | Slots filled by your registration configuration |
Labels in $metadata | Generic: "Ticket owner: Info 47" |
| Meaning | Defined by your setup, not by us |
| Numbering | Starts at TicketOwnerInfo2; there is no TicketOwnerInfo1 |
You have to supply the mapping. We cannot label these for you, because what
TicketOwnerInfo47holds depends on how your registration form was configured. Ask whoever configured it for the field mapping, record it in your warehouse as a lookup, and rename the columns as you stage them. A staging model that renamesTicketOwnerInfo47tovisitor_job_roleis the difference between a usable warehouse and an unusable one.Most extracts load only the handful of these that their forms actually use. Loading all 107 is rarely useful.
Types and how they behave
Type in $metadata | Notes |
|---|---|
Edm.Int32 / Edm.Int64 | Ids and counts. |
Edm.String | Text — including the status columns, see below. |
Edm.Boolean | Flags. Some are nullable, so treat null as its own case. |
Edm.Decimal | Money, with precision 14 and scale 6. Load as decimal, never float. |
Edm.Guid | The *UniqueId columns. |
Edm.DateTimeOffset | Points in time, with an offset. |
Edm.Date / no-offset date-times | Calendar dates and internal timestamps — see the warning below. |
Status columns are strings holding tokens
TicketStatusType, SaleStatus, ArticleType and their relatives are Edm.String carrying UPPER_SNAKE_CASE tokens such as ASSIGNED, PAID or SERVICE_FEE. They were numeric enums once; the hub converted them to text deliberately, so that consumers see a readable label instead of an integer whose meaning lives in someone else's source code.
Most of them have a closed, documented value set — Column values lists every one, and marks the handful that are pass-through columns whose values come from the source system rather than from a fixed list.
Filter them as ordinary strings: ?$filter=SaleStatus eq 'COMPLETED'.
Time zones — the one that silently corrupts reports
Two different kinds of temporal column exist, and they must be handled differently:
| Kind | Columns | Handle by |
|---|---|---|
| Offset-aware instants | TicketSoldAt, TicketUsedAt, TicketRegisteredAt, TicketAssignedAt, SaleTimestamp, PaymentTimestamp, TicketCancelledOn, TicketShippedOn, FaireventStartDate, FaireventEndDate, LegitimationValidFrom/ValidTo, PromotionCode* timestamps, and Timestamp on usages | Converting to whatever zone you report in. These carry an offset and are unambiguous. |
| Plain dates and internal timestamps | TicketOwnerBirthDate, TicketOwnerLastActionOn, LastRefreshedAt | Do not time-zone convert these. |
TicketOwnerBirthDateis a calendar date, not an instant. Converting it across zones shifts birthdays by a day and quietly corrupts every age bracket you compute. Load it as a date type, not a timestamp.
LastRefreshedAtandTicketOwnerLastActionOncarry no offset either. UseLastRefreshedAtonly as an opaque watermark for incremental loading — compare it against the last value you saw, and do not present it to users as a local time.
There is no fair-local time column. The event's own time zone is not exposed, so scans are not re-projected into local event time for you. If your report needs "9am on the second day of the fair", apply the event's zone yourself — you know which city it was in.
Nulls
Nullability is stated per column in $metadata, and null is common and meaningful:
- Person and address fields are null where the visitor did not supply them.
CurrencyShortcan be null on rows with no monetary value.- Legitimation, promotion-code, upselling and cross-selling families are null on tickets they do not apply to.
- Several status columns are nullable, and null means "not applicable here" rather than a state.
- A null boolean is neither true nor false —
$filter=SomeFlag eq falsewill not match it. Filtereq nullexplicitly when you mean it.
Getting the full list
GET /odata/v1/$metadata returns every column with its type, nullability, key membership and business label. See Field reference.