Get surveys
What visitors told you, joined back to who they are. One row per selected option — read the grain note before you count anything.
Because survey answers join to the ticket, any answer can be segmented by anything the ticket knows: article, event, statistics group, country, or how the ticket was issued.
Grain and key
| Grain | one row per selected option (selection grain) |
| Natural key | MandatorInternalId, AnswerInternalId, SelectionOrdinal |
| Page size | 500 |
| Columns | 32 |
| Watermark | LastRefreshedAt — incremental loading possible |
The columns most extracts start with
| Column | Type | Notes |
|---|---|---|
SubmissionInternalId | int | One completed survey. Count distinct for responses. |
AnswerInternalId | int | One answer to one question. Count distinct for answers. |
SelectionOrdinal | short | Which selected option this row represents. |
QuestionText | string? | The question as the visitor saw it. |
QuestionType | string? | The question's form type. Pass-through — no fixed list. |
IsMultiSelect | bool | Whether several options were possible. |
IsRequired | bool | Whether the question was mandatory. |
Answer | string? | The raw lookup key. |
AnswerDisplayValue | string? | The label the visitor saw. Report on this one. |
IsCustomAnswer | bool | A free-text answer rather than a chosen option. |
AnswerSource | string? | Where the answer came from. Pass-through. |
SubmittedAt | datetimeoffset | When it was answered. |
Language | string? | Which language they answered in. |
TicketInternalId | int | Join key back to Tickets. |
LastRefreshedAt | datetime | Watermark. No offset — do not convert. |
Traps specific to this dataset
-
Selection grain is the trap. A multi-select answer is not one row with a comma-separated list; it is one row per selected option. Someone ticking four boxes produces four rows. Counting rows overcounts, sometimes by a lot.
You want Count Responses (completed surveys) distinct SubmissionInternalIdAnswers (question-level) distinct AnswerInternalIdSelections (option-level) rows -
Group on
AnswerDisplayValue, notAnswer.Answerholds the raw lookup key, which is stable but meaningless on a chart.AnswerDisplayValueis the label the visitor actually read. -
Free-text answers need separate handling.
IsCustomAnswerrows contain whatever the visitor typed — do not group on them as if they were a controlled vocabulary. -
No visitor context here. Name, contact data and article are deliberately not in this dataset — join
Tickets.
Joining
Join to Tickets on MandatorInternalId, TicketInternalId.
Request
No request body.
$select(string): the columns to load.$filter(string): an OData predicate. This dataset carriesLastRefreshedAt, so it can be loaded incrementally.$orderby(string): sort order.$top(integer): maximum rows, capped at 1000.$count(boolean): include the total count.$skiptoken(string): server-generated paging token.
Response
@odata.context(string): the metadata URL describing this payload.value(array): the page of selection rows.@odata.nextLink(string, optional): the next page; absent on the last one.@odata.count(integer, optional): present only when$count=true.
The grain will catch you out
One row is one selected option, not one answer. Somebody ticking four boxes on a multi-select question produces four rows.
| You want | Count |
|---|---|
| Responses (completed surveys) | distinct SubmissionInternalId |
| Answers (question level) | distinct AnswerInternalId |
| Selections (option level) | rows |
Group and report on AnswerDisplayValue, the label the visitor actually read — Answer holds the raw lookup key, which is stable but meaningless on a chart.
Notes
Page size is 500. @odata.count counts selections, so it is not your response count.
QuestionType, AnswerSource and Characteristic are pass-through columns — their value sets come from your own survey configuration rather than from a fixed list. See Datasets → Column values.
Authentication#auth
This endpoint does not require authentication.
Headers#headers
Required. A client-credentials token carrying the scope ticketinghub-api and no subject. See Authentication.
Optional. JSON is returned by default.
Query parameters#query
Comma-separated list of columns to load. Carry TicketInternalId so the answers can be joined back and segmented; report on AnswerDisplayValue rather than the raw Answer key.
OData predicate. This dataset exposes LastRefreshedAt, so a watermark filter gives you an incremental load.
Sort order. Note that it also changes the order the keyset walk runs in.
Maximum number of rows. Capped at 1000; a higher value is rejected with 400. A smaller value lowers the page size but cannot raise it above 500.
Include the total row count in @odata.count. Because of the selection grain this counts ticked options, not responses — count distinct SubmissionInternalId for that.
Server-generated keyset paging token. Note that this dataset has a three-part key, so the walk runs in alphabetical key order.
Offset paging. Supported but discouraged — use @odata.nextLink instead.