Profiles
If you pass the same identifier across different polls, quizzes, and forms for the same real person, look up everything known about them as one derived profile.
Pass the same value as voter_identifier, taker_identifier, or respondent_identifier to link activity to one person — useful for progressive profiling (enriching one contact record a bit at a time across multiple touchpoints). There's no separate Profile resource to create — a profile is just a view over the votes/submissions/attempts that already reference an identifier, computed at read time.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /profiles | API key | List every identifier with activity, newest-active first |
| GET | /profiles/{identifier} | API key | Get the derived profile for an identifier |
/profiles/{identifier}
API key
Get a profile
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/profiles/usr_123
{
"data": {
"identifier": "usr_123",
"attributes": {
"favorite-color": "Blue",
"company_name": "Acme Inc"
},
"activity": [
{ "type": "quiz_attempt", "resource_type": "quiz", "resource_identifier": "js-basics", "resource_title": "JS Basics", "score": 8, "total": 10, "created_at": "2026-08-21T18:01:09.000000Z" },
{ "type": "form_submission", "resource_type": "form", "resource_identifier": "feedback", "resource_title": "Feedback", "answers": [{ "label": "Company Name", "value": "Acme Inc" }], "created_at": "2026-08-20T09:12:00.000000Z" },
{ "type": "vote", "resource_type": "poll", "resource_identifier": "favorite-color", "resource_title": "Favorite Color", "value": "Blue", "created_at": "2026-08-19T14:03:00.000000Z" }
],
"quiz_attempts": [
{ "quiz_identifier": "js-basics", "quiz_title": "JS Basics", "score": 8, "total": 10, "created_at": "2026-08-21T18:01:09.000000Z" }
],
"last_active_at": "2026-08-21T18:01:09.000000Z"
}
}How attributes is built
- Each poll vote becomes one attribute, keyed by the poll's
identifier, valued as the chosen option's label. - Each form answer becomes one attribute, keyed by the field's label slugified into
snake_case(e.g. "Company Name" →company_name); amulti_choiceanswer's selected options are joined with,. - If the identifier answers the same-named attribute more than once, the most recent answer wins.
- Quiz attempts are listed separately, not folded into
attributes— a graded score isn't a labeled trait the way a poll/form answer is. - An identifier with no activity yet returns
200 OKwith emptyattributes/activity/quiz_attemptsrather than404.
activity is every vote/submission/attempt under the identifier, newest first — the "who submitted what, and when" view, separate from the reduced attributes map.
/profiles
API key
List profiles
Every identifier with at least one vote/submission/attempt on your account, each returned as a full profile object, most-recently-active first. This is the intended way to pull data into a CRM: poll it on a schedule with since set to your last run, and only identifiers that changed come back.
| Query param | Type | Rules |
|---|---|---|
| since | string | Optional, a parseable date/time — only identifiers active at or after this moment |
| per_page | integer | Optional, 1-100, default 25 |
curl -H "X-API-Key: qna_..." "https://qnaapi.com/api/v1/profiles?since=2026-08-25T00:00:00Z&per_page=50"
{
"data": [ /* profile objects, same shape as GET /profiles/{identifier} */ ],
"meta": { "current_page": 1, "per_page": 50, "total": 132, "last_page": 3 }
}data-qna-identifier="..." on the embedded form to your own user/customer ID — polls and quizzes fall back to an anonymous per-browser ID when it's left blank; forms stay fully anonymous unless you set it.