---
title: Custom attributes
path: concepts/custom-attributes
status: published
---

# Custom attributes

Custom attributes let a tenant attach its own fields to users, groups, and OUs —
beyond ScaiKey's built-in profile — and optionally surface chosen ones as OIDC
token claims.

## Definitions vs values

- A **definition** (`CustomAttributeDefinition`) declares that an attribute
  *exists* for a tenant: its `name` (the key/claim name), `display_name`,
  `entity_type` (`USER` / `GROUP` / `OU`), `data_type`
  (`STRING`, `INTEGER`, `BOOLEAN`, `DATE`, `DATETIME`, `EMAIL`, `URL`, `ENUM`,
  `STRING_ARRAY`), and flags: `required`, `searchable`, `visible_in_ui`,
  `include_in_token`.
- A **value** is what's stored on a specific user/group/OU (in its
  `custom_attributes`), e.g. `{"cost_center": "CC-1"}`.

## Managing definitions

Admin console: **Custom Attributes** (pick the tenant if you're a super-admin).
Create/delete definitions and toggle *Include in token* / *Visible in UI* inline.
Deleting a definition doesn't erase values already stored; it stops the field
being offered and, if it was emitted, stops the claim.

API (tenant-scoped): `GET/POST /api/v1/tenants/{tenant_id}/attributes`,
`PATCH/DELETE /api/v1/tenants/{tenant_id}/attributes/{id}`.

## Attributes as token claims

An attribute is emitted as an OIDC claim only when **all** hold:

1. its definition has `include_in_token = true`,
2. its `entity_type` is `USER`,
3. the user has a value stored for it,
4. the requesting token has the `profile` scope, and
5. the attribute name is **not** a reserved claim.

**Reserved names are silently dropped** from tokens even if flagged — anything a
consumer might read as authorization: `role(s)`, `permissions`, `scope(s)`,
`is_admin` / `is_super_admin` / …, `partner_id(s)`, `tenant_id`, `groups`,
`distribution_groups`, the standard JWT registered claims (`sub`, `iss`, `aud`,
…), and `created_at`. Defining `include_in_token` on such a name is treated as a
configuration mistake, not a way to spoof an authorization signal.

Values from LDAP-synced tenants land in `custom_attributes` during sync; local
tenants set them via the user/group admin APIs.

See also [Applications](/docs/scaikey/concepts/applications) and, for reading
claims, the [external-app integration guide](/docs/scaikey/tutorials/external-app-oidc#claims-by-scope).
