Profile fields and custom attributes
Define profile fields, choose who manages them, and understand how console edits, identity providers and imports use the schema.
Every user carries a profile: a set of attributes beyond the basics of name and email — department, office, cost centre, line manager, contract end date, or anything else your organisation wants to record. 31 base attributes are built in and governed centrally (login, name fields, address fields, locale, timezone, enterprise fields like manager and cost centre, and more); anything beyond those is a custom attribute you define yourself, with a real data type and real constraints, in the same schema.
Where it lives
People and profiles → Profile fields, at /iam/profile-schemas. One table lists every attribute — base and custom together — with a filter by user type and a toggle to show inactive ones, and a stat row summarising the schema. Pressing an attribute opens an editor for its constraints, its self-service permission, its sensitivity, its mastering source, and its SCIM path. A base attribute's name, type and SCIM path are read-only there — you govern it, you don't redefine it — while a custom attribute is fully yours to define.
The table displays the saved definition. To verify an integration, change a test value at the source, run its sync or sign-in flow, and inspect the actual resulting user profile.
Choose the identity provider responsible for a field
Add or edit an attribute, set Mastered by to Identity provider, and search the Identity provider field by its configured name. The list contains this organisation's OpenID Connect, WS-Federation and SAML providers. Select the intended provider and save. Reopen the attribute to confirm the selection; the table also shows the provider name.
New selections require an enabled provider from this organisation. If no providers are available, configure one under Sign-in providers, return to the editor and refresh. A previously selected disabled or deleted provider is shown as unavailable; changing it requires an enabled replacement. Switching the mastering source back to the console clears the provider selection.
The 31 base attributes
These exist for every organisation from the start, each with a sensible default type, storage location, requiredness and self-service permission — and every governance setting on a base attribute (required, self-service permission, sensitivity, pattern, and so on) can be overridden per tenant, except its name, its underlying type and its SCIM path.
| Attribute | Type | Default self-service | SCIM path |
|---|---|---|---|
login | string | read-only | userName |
email | read-only | emails[type eq "work"].value | |
firstName / lastName | string | read-only | name.givenName / name.familyName |
displayName, nickName, title | string | read-write / read-write / read-only | displayName, nickName, title |
mobilePhone, primaryPhone | phone | read-write | phoneNumbers[type eq "mobile"/"work"].value |
streetAddress, city, state, zipCode, countryCode | string / country_code | read-write | addresses[…] |
preferredLanguage, locale, timezone | language_code / string / timezone | read-write | preferredLanguage, locale, timezone |
userType | enum (5 values) | read-only | userType |
employeeNumber, costCenter, organization, division, department | string | read-only | enterprise:* |
managerId, manager | string | read-only | enterprise:manager.value / .displayName |
Setting managerId refreshes the paired manager attribute automatically from the named account, when that account belongs to the same organisation.
Custom attributes: 15 data types, real constraints
A custom attribute gets a name, a type, and whatever constraints apply to that type. Fifteen data types are supported:
string, integer, number, boolean, date, enum,
string_array, integer_array, number_array,
email, url, phone, country_code, language_code, timezoneThe editor supports these constraints:
- Required — checked when validating a complete profile; partial updates need not resend unrelated fields.
- Unique — enforced per organisation, against a side table of reserved values, so two people cannot end up with the same value on a unique attribute.
- Enum membership.
- Pattern — a regular expression (RE2), checked against the value.
- Minimum and maximum length, for text.
- Minimum and maximum value, for numbers.
- Default value.
Self-service and administrator profile updates validate the supplied values. The SCIM path normalises known attributes and may drop invalid fields with a diagnostic; it also retains provider housekeeping keys. Staged imports use a separate preview and source-priority mechanism. Validate the paths you plan to use rather than treating a schema definition as evidence that every connector applies identical rules.
Per-attribute governance: who can touch it, and how sensitive it is
| Field | What it controls |
|---|---|
| Self-service permission | hidden (absent from the self-service profile entirely), read_only (visible, not editable by the person themselves) or read_write. Enforced on GET/PUT /users/me/profile and on PUT /users/me. |
| Sensitivity | Setting sensitivity = masked masks the value in the user-detail screen (unless the caller holds users:write), in self-service read-only rows, and in audit metadata. This is real for those three surfaces and not yet applied to exports or SCIM reads — a masked attribute can still appear in an export or a SCIM response today. |
| Master source | admin, scim, federation or hr. An attribute mastered by a non-admin source refuses a console edit with iam.attribute_managed. There is no ordered priority between sources yet on this surface — for that, see the per-source priority ordering staged import uses, which is a related but separate mechanism. |
| SCIM path | Where the attribute is read and written over SCIM. For base attributes this is fixed; for a custom attribute you set it — though a custom attribute's SCIM path is currently stored and shown, not yet read from an inbound SCIM request body by that path (only the base mapping is fully wired on the inbound side). |
The two old API surfaces still answer — from the same store
If you have scripts or integrations built against either of the two older surfaces, they keep working unchanged: both now read and write through the unified schema, and both render their traditional response shapes on the way out.
| Old surface | Used to be | Now |
|---|---|---|
GET/POST /organizations/{orgID}/directory/schema | A separate table (profile_schema_attributes), API-only, validated data. | Answers from the unified schema. Definitions from the old table were carried forward into it; there is no reader or writer left on the old table. |
GET/POST /organizations/{orgID}/profile-schemas (per user type), and the values under .../users/{userID}/profile | A separate table (user_profile_schemas), the console screen this article used to describe, which validated nothing. | Answers from the unified schema, rendering the old four-word type vocabulary (string, integer, boolean, enum, etc.) on read and mapping it back on write, so nothing that called this API has to change. It now validates, because it's the same store as everything else. |
Both old tables are frozen — nothing reads or writes them any more — and are expected to be dropped in a later migration once this has run for a release. The console screen this article used to point you at for the per-user-type editor was deleted; the nav entry and any link to it now lands on the unified editor instead.
What actually reads a profile attribute
- Group rules and expressions read them, as
profile.<name>,user.<name>, oruser.profile.<name>depending on whether you're in the field/operator model or the expression engine — see Groups and automatic membership and The expression language behind group rules, policies and mappings. - Sign-on policy conditions and profile/app mappings can now read attributes through the same expression engine, in the
policyandmappingscopes respectively. displayNamestill has special meaning — it's what inbound provisioning filters an external identity system on.- Staged imports respect master-priority protection on every attribute — see Staged import with preview.
Attribute matching in a field/operator group rule is still case-sensitive on the value, exactly as before: Sales and sales are different departments as far as a rule is concerned.
Endpoints, for scripting directly against the unified schema
| Method | Path | Notes |
|---|---|---|
| GET | /organizations/{orgID}/schemas/{scope}?user_type= | The merged catalogue for a scope, plus the list of supported data types. |
| POST | /organizations/{orgID}/schemas/user/attributes | Define a custom attribute. 400 iam.schema_attribute_invalid, 409 iam.schema_attribute_exists. |
| PUT | /organizations/{orgID}/schemas/{scope}/attributes/{name} | Update a custom attribute, or govern a base one. 409 iam.schema_attribute_not_unique with the offending values, if you toggle uniqueness on against data that already collides. |
| DELETE | /organizations/{orgID}/schemas/{scope}/attributes/{name} | Custom: deleted outright. firstName/lastName: deactivated rather than deleted. Any other base attribute: refused, iam.schema_attribute_immutable. |
| GET/PUT | /users/me/profile | Self-service read/write, governed by self-service permission and validated by the catalogue. |
| PATCH | /organizations/{orgID}/users/{userID}/profile | Administrative write. Mastered attributes refuse with iam.attribute_managed; uniqueness is enforced. |
User types, unchanged
Five user types still exist — EMPLOYEE, CONTRACTOR, VENDOR, PARTNER, SERVICE_ACCOUNT — and every account Qorionix creates for you is still written as EMPLOYEE, with no endpoint changing it afterwards, unless you provision from an external directory that sets it. That part of the older article's advice stands: define the employee schema attributes first, and treat the other four as available rather than in active use unless you're syncing from a directory. See Users and the directory for the rest of the user record.