Docs
Pricing Log in
Documentation

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.

MethodEndpointAuthDescription
GET/profilesAPI keyList every identifier with activity, newest-active first
GET/profiles/{identifier}API keyGet the derived profile for an identifier
GET /profiles/{identifier} API key

Get a profile

REQUEST
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/profiles/usr_123
200 OK
{
  "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); a multi_choice answer'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 OK with empty attributes/activity/quiz_attempts rather than 404.

activity is every vote/submission/attempt under the identifier, newest first — the "who submitted what, and when" view, separate from the reduced attributes map.

GET /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 paramTypeRules
sincestringOptional, a parseable date/time — only identifiers active at or after this moment
per_pageintegerOptional, 1-100, default 25
REQUEST
curl -H "X-API-Key: qna_..." "https://qnaapi.com/api/v1/profiles?since=2026-08-25T00:00:00Z&per_page=50"
200 OK
{
  "data": [ /* profile objects, same shape as GET /profiles/{identifier} */ ],
  "meta": { "current_page": 1, "per_page": 50, "total": 132, "last_page": 3 }
}
To tie responses to a known person (rather than an anonymous visitor) using the dashboard's generated embed snippets, set 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.