Profiles API

Profiles API — Overview

Reusable bundles of restrictions, feature toggles and tag defaults that you assign to team members per event — the configuration layer behind the Qflow check-in app.

A profile is a named, reusable bundle of everything that governs how the check-in app behaves for a team member: which capabilities are locked (restrictions), which features are on and whether the member can change them on the device (settings), and the default filter / check-in / statistics tags. Build a profile once — "Door Scanner", "VIP Desk", "Roaming Supervisor" — then assign it to one, several, or every team member working an event.

Profiles are the configuration layer that sits on top of Team: Team decides who works an event, Profiles decides what they can do once they're there.

URL pattern

All endpoints on this page live under:

https://api.qflowhub.io/profiles/v1/<endpoint>

Every request needs both an OAuth bearer token AND an Ocp-Apim-Subscription-Key header — see Authentication.

What it does

Quick start — build a door profile and assign it

Create a profile that locks the device down to scanning and hides everything else:

curl -X POST 'https://api.qflowhub.io/profiles/v1' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Door Scanner",
    "restrictions": ["restrict_settings", "restrict_stats", "restrict_signout 4821"],
    "settings": { "ContinuousScan": { "value": true, "locked": true } }
  }'

Save the returned id, then assign it to two door staff for an event:

curl -X POST 'https://api.qflowhub.io/profiles/v1/assign' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "profileId": "<profileId>",
    "eventId": "<eventId>",
    "teamMemberIds": ["<member1>", "<member2>"]
  }'

Both devices pick up the profile on their next sync. The members must already be assigned to the event via the Team API first.

Read in this order

  1. Profiles — the profile object and its CRUD lifecycle.
  2. Restrictions & settings — the catalogue of what a profile can carry.
  3. Assignment — fan out a profile across an event's team.
  4. Resolved config — preview the merged result a device receives.

For full request/response schemas + try-it, see the auto-generated API Reference.

Profiles API

Profiles API — Profiles

The profile object and its lifecycle — create, read, update, delete and duplicate the reusable bundle of restrictions, settings and tags.

A profile is owned by your account and reused across events. It holds four things: a name, a set of restrictions, a map of settings (feature toggles), and three lists of default tags. Nothing in a profile references an event or a team member — that link is made separately when you assign it.

The profile object

{
  "id": "8f2c…",
  "name": "Door Scanner",
  "restrictions": ["restrict_settings", "restrict_stats", "restrict_signout 4821"],
  "settings": {
    "ContinuousScan": { "value": true, "locked": true },
    "OfflineMode":    { "value": false, "locked": false }
  },
  "filterTags":  [{ "name": "VIP", "type": "tickettype" }],
  "checkinTags": [{ "name": "Checked-Door-A", "type": "tag" }],
  "statsTags":   [],
  "created": "2026-06-16T09:00:00Z"
}
  • restrictions — an array of restriction codes; two may carry an inline value. See Restrictions & settings.
  • settings — feature toggles keyed by name, each with value (on/off) and locked (whether the member can change it on the device). The tag-lock flags FilterTagsLocked, CheckinTagsLocked and StatsTagsLocked live here too.
  • filterTags / checkinTags / statsTags — name + type pairs, matched against the event's own tags when the profile is assigned.

id and created are server-assigned and ignored on input.

Lifecycle

List

GET /profiles/v1 — every profile on your account, newest first.

Get one

GET /profiles/v1/{id} — a single profile by id.

Create

POST /profiles/v1 — a new, unassigned profile.

Update

PUT /profiles/v1/{id} — replace the restrictions, settings and tags.

Delete

POST /profiles/v1/delete — delete the profile and clear it from every member.

Duplicate

POST /profiles/v1/duplicate — copy a profile as a starting point for a variant.

Update re-syncs every assigned device

PUT /profiles/v1/{id} is a full replace of the bundle — send the complete restrictions, settings and tag lists you want, not a delta. Every device the profile is currently assigned to is re-synced immediately, so the change lands without waiting for the next event.

Update is idempotent: sending the same body twice leaves the same end state.

Delete

Delete takes the id in the body so callers without HTTP DELETE support work:

curl -X POST 'https://api.qflowhub.io/profiles/v1/delete' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{ "id": "<profileId>" }'

It removes the profile and clears it from every team member it was assigned to; those devices re-sync and drop the profile. A repeat delete of an already-deleted profile returns 404.

Duplicate

Duplicate creates an unassigned copy with the same restrictions, settings and tags, named "<original> (copy)". Use it to branch a one-off variant for a single event without touching the shared profile:

curl -X POST 'https://api.qflowhub.io/profiles/v1/duplicate' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{ "id": "<profileId>" }'

Next

Try it
GET/ POST/ GET/{id} PUT/{id} POST/delete POST/duplicate
Profiles API

Profiles API — Restrictions & settings

The catalogue of restriction codes and feature toggles a profile can carry, including the two restrictions that take a value.

A profile's behaviour comes from two fields: restrictions (capabilities you lock down) and settings (feature toggles you preset). This page is the reference for both.

Restrictions

restrictions is an array of codes. Most are simple on/off locks — including the code in the array turns the lock on:

Code Locks
restrict_settings Access to the in-app settings screen
restrict_filtering Changing the guest-list filter
restrict_switchevents Switching to a different event
restrict_checkout Checking guests back out
restrict_changeguestinfo Editing guest details
restrict_overrideblock Checking in a blocked guest
restrict_scanneronly The device to the scan window only
restrict_stats Viewing the statistics screen

Restrictions that take a value

Two restrictions accept an inline value — the code, a single space, then the value:

Code Value Example
restrict_signout A PIN required to sign out. Omit the value to lock sign-out entirely. restrict_signout 4821
restrict_addguest The maximum number of guests the member may add. Omit the value to block adding guests entirely. restrict_addguest 5
"restrictions": ["restrict_settings", "restrict_signout 4821", "restrict_addguest 5"]

restrict_signout 4821 lets a supervisor still sign the device out by entering 4821; bare restrict_signout blocks sign-out outright. Likewise restrict_addguest 5 caps additions at five, while bare restrict_addguest blocks them.

Note

Codes are validated on create and update — an unrecognised code returns 400. Only the codes in the tables above are accepted.

Settings

settings is a map keyed by setting name. Each entry has two booleans:

"settings": {
  "ContinuousScan": { "value": true,  "locked": true },
  "OfflineMode":    { "value": false, "locked": false }
}
  • value — the on/off state the device adopts.
  • locked — when true, the member can't change it on the device; when false, it's a suggested default they can still override.

Setting names are fixed identifiers in PascalCase — ContinuousScan, CheckoutMode, PrintOnScan, RepeatScan (scanning); SearchLastName, ShowGuestNotes, StartGuestList (guest-list display); OfflineMode, DisableSounds, NfcScanMode (device options); and the tag-lock flags FilterTagsLocked, CheckinTagsLocked, StatsTagsLocked. Use the name exactly as listed; the API canonicalises casing on save, so an unknown name is the only way to get it wrong.

Older app versions are covered automatically

Restrictions set on a profile are delivered to current apps through the resolved profile, and to older app versions that can't read profiles through the underlying control-message channel — so the same lock-downs apply on every device regardless of version, including the sign-out PIN and guest cap. You don't call anything extra; assigning the profile is enough.

Next

  • Assignment — push the profile to an event's team.
  • Resolved config — see the effective restrictions a device ends up with.
Profiles API

Profiles API — Assignment

Assign a profile to one, many, or all team members on an event — explicit fan-out — plus per-event tag overrides.

Assignment is the link between a profile and the team. It's always per (team member, event): the same profile can drive a dozen members on one event and a different set on another. You fan out explicitly — you list the member ids you want, and each gets its own assignment.

Note

A team member must already be assigned to the event via the Team API before a profile assignment takes effect on their device.

Assign

POST /profiles/v1/assign — assign one profile to a list of members on an event:

curl -X POST 'https://api.qflowhub.io/profiles/v1/assign' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "profileId": "<profileId>",
    "eventId": "<eventId>",
    "teamMemberIds": ["<member1>", "<member2>", "<member3>"]
  }'
  • One member — pass a single id.
  • Several — pass the subset you want.
  • All — pass every member's id. There's no "whole event" shortcut by design: fan-out is explicit so the assignment set is always exactly what you sent.

Each (member, event) gets its own assignment, replacing any profile previously assigned to that member on that event. Every affected device syncs immediately. Assign is idempotent — sending the same request again is a no-op, so a retry after a timeout never double-assigns.

Unassign

POST /profiles/v1/unassign — clear the assignment for a list of members on an event:

curl -X POST 'https://api.qflowhub.io/profiles/v1/unassign' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "eventId": "<eventId>",
    "teamMemberIds": ["<member1>", "<member2>"]
  }'

Each member's device re-syncs and drops the profile, clearing its restrictions. Idempotent per member — clearing a member who has no assignment is a successful no-op.

List assignments

Two read endpoints, depending on which way you're looking:

By event

GET /profiles/v1/event/{eventId}/assignments — every member on the event that has a profile, with any tag overrides.

By profile

GET /profiles/v1/{id}/assignments — every (member, event) a profile is currently assigned to.

Per-event tag overrides

A profile carries default tags, but a single assignment can override them for one event without touching the shared profile — handy when "Door Scanner" needs a different filter at one venue:

curl -X PUT 'https://api.qflowhub.io/profiles/v1/assignment/tags' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "eventId": "<eventId>",
    "teamMemberId": "<member1>",
    "filterTags": [{ "name": "Table 1-20", "type": "tag" }]
  }'

Send a tag list to override it; omit a field (or send null) to clear that override and fall back to the profile's own tags. Only the tag lists can be overridden per assignment — restrictions and settings always come from the profile.

Next

Try it
GET/event/{eventId}/assignments GET/{id}/assignments POST/assign POST/unassign PUT/assignment/tags
Profiles API

Profiles API — Resolved config

Preview the effective configuration a member's device receives after the profile and any per-event overrides are merged.

The resolved endpoint answers a single question: what will this member's device actually get on this event? It returns the assigned profile merged with any per-event tag overrides — the exact payload the check-in app reads — so you can confirm a setup without picking up a device.

Get the resolved config

GET /profiles/v1/resolved/event/{eventId}/member/{teamMemberId}:

curl 'https://api.qflowhub.io/profiles/v1/resolved/event/<eventId>/member/<teamMemberId>' \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Ocp-Apim-Subscription-Key: <subscription_key>'
{
  "profileId": "8f2c…",
  "profileName": "Door Scanner",
  "settings": {
    "ContinuousScan": { "value": true, "locked": true }
  },
  "restrictions": ["restrict_settings", "restrict_stats", "restrict_signout 4821"],
  "filterTags":  [{ "name": "Table 1-20", "type": "tag" }],
  "checkinTags": [{ "name": "Checked-Door-A", "type": "tag" }],
  "statsTags":   []
}

What "resolved" means

  • Settings and restrictions come straight from the assigned profile.
  • Tags are the per-event override if one is set on the assignment, otherwise the profile's own defaults — so filterTags above reflects the override from the Assignment example, not the profile default.

If the member has no assignment on the event, profileId and profileName are null and the lists come back empty — a clean signal that nothing is applied.

When to use it

  • Verify a fan-out assignment landed the way you intended, on a specific member.
  • Debug a member reporting the wrong lock-downs — compare resolved against the profile.
  • Audit what a device is operating under without interrupting it.

This is the same merge the device performs, so it's the source of truth for "what's on that device right now".

Back to

Try it
GET/resolved/event/{eventId}/member/{teamMemberId}
Profiles API

API Reference

Interactive reference for the Profiles surface — reusable bundles of restrictions, feature toggles and tag defaults that you assign to team members per event.