# Custom Properties

In addition to built-in fields such as `last_login_at`, OAA supports custom-named properties for users, resources, groups, and other entities in a payload. Custom properties are validated against a set of custom property definitions as part of the JSON payload.

To use custom properties:

1. Include a definition of all properties as part of the payload push. The definition sets the type for the property.
2. Set the custom properties for each object.

### Use cases

Built-in properties (such as `last_login`, `created_at`, and `is_active` for local users) enrich Veza graph entities with additional metadata. These built-in properties are described in the template documentation for [applications](/4yItIzMvkpAvMVFAamTf/developers/api/oaa/templates/custom-application-template.md) and [identity providers](/4yItIzMvkpAvMVFAamTf/developers/api/oaa/templates/custom-identity-provider-template.md).

If a built-in property doesn’t exist for the provider you are modeling, you can define custom properties in the OAA payload. This can enable sophisticated queries on many possible fields (such as `encryption_enabled`, `password_last_used_at`). Custom properties have a declared data type and are indexed and filterable, like any built-in property.

> For example, you could use the custom string property *"state"* for an app that can be either "active," "suspended," or "disabled." If there's only one state, you could instead use a boolean ("active": "false").

### Custom property types

Custom Properties are defined as part of the `custom_property_definition` section and require a type. Typing the data allows Veza to provide a better index and search experience. For example, the `TIMESTAMP` type enables date-relative filters such as "in the last 30 days."

Allowed types are `NUMBER`, `STRING`, `STRING_LIST`, `TIMESTAMP,` and `BOOLEAN`.

* A `type` is required and permanent.
* Dates must be in [RFC3339 format](https://datatracker.ietf.org/doc/html/rfc3339), which can include a timezone offset or use UTC (`2021-01-01T22:47:31-07:00`, `2021-01-01T22:47:31Z`).
* The maximum length of a string is 4096 characters.

In the Veza UI, underscores in property names are replaced by spaces, and first letters are capitalized (`is_licensed` > *Is Licensed*).

If a custom property name collides with an existing built-in property, Veza will add the "Custom" prefix (`ID` > *Custom Id*).

In the Query Builder API, custom properties are prefixed with `custom_` in responses and must be prefixed with `custom_` when used in filter statements.

#### Modifying custom properties

Once pushed, properties and types can't be altered. You can re-submit the payload with additional custom property definitions to add new properties.

* Property values on entities can be modified by pushing a complete payload with new `custom_properties` or using [incremental update](/4yItIzMvkpAvMVFAamTf/developers/api/oaa/best-practices/incremental-updates.md) operations.
* The provider must be deleted and pushed again to remove properties from a definition or change their types.
* The original custom property definition is not required in future submissions but should be saved for later reference.

### OAA custom property examples

Here are some example custom property definitions for the [custom application](/4yItIzMvkpAvMVFAamTf/developers/api/oaa/templates/custom-application-template.md) and [custom identity provider](/4yItIzMvkpAvMVFAamTf/developers/api/oaa/templates/custom-identity-provider-template.md) templates.

The [Python SDK](/4yItIzMvkpAvMVFAamTf/developers/api/oaa/python-sdk.md) also supports creating and setting custom properties.

#### Custom properties with `oaaclient` Python SDK

```python
app = CustomApplication(name="Demo", application_type="Demo")
# Define a new local user string property `email`
app.property_definitions.define_local_user_property("email", OAAPropertyType.STRING)

local_user = app.add_local_user(name="name", unique_id="user_id")
# set the property by name
local_user.set_property("email", "user@example.com")
```

#### Custom properties for a custom application

A custom property definition sets possible properties, their types, and the application or resource type they can apply to. The following entities can have custom properties:

| Entity             | Key                             | Notes                                                  |
| ------------------ | ------------------------------- | ------------------------------------------------------ |
| Custom Application | `application_properties`        | Scoped by `application_type`                           |
| Custom User        | `local_user_properties`         |                                                        |
| Custom Group       | `local_group_properties`        |                                                        |
| Custom Role        | `local_role_properties`         |                                                        |
| Role Assignment    | `role_assignment_properties`    | Properties on the role assignment itself, not the role |
| Access Credential  | `local_access_creds_properties` |                                                        |
| Custom Resource    | `resources`                     | Scoped by `resource_type`                              |

The following example shows a custom property definition for GitLab:

```json
  "custom_property_definition": {
    "applications": [
      {
        "application_type": "GitLab",
        "application_properties": {},
        "local_user_properties": {
          "id": "NUMBER",
          "bot": "BOOLEAN",
          "is_licensed": "BOOLEAN",
          "state": "STRING"
        },
        "local_group_properties": {},
        "local_role_properties": {},
        "role_assignment_properties": {
          "assigned_at": "TIMESTAMP",
          "expires_at": "TIMESTAMP"
        },
        "local_access_creds_properties": {
          "scope": "STRING",
          "rotation_period_days": "NUMBER"
        },
        "resources": [
          {
            "resource_type": "project",
            "properties": {
              "id": "NUMBER",
              "visibility": "STRING"
            }
          }
        ]
      }
    ]
  }
```

Properties are set on users and resources in `custom_properties`:

```json
{
  "name": "support-bot",
  "identities": ["support@cookie.ai"],
  "groups": null,
  "is_active": true,
  "created_at": "2022-01-25T18:55:19.146Z",
  "last_login_at": null,
  "deactivated_at": null,
  "password_last_changed_at": null,
  "tags": [],
  "custom_properties": {
    "id": 7,
    "is_licensed": false,
    "state": "active",
    "bot": true
  }
}
```

#### Custom properties for a custom identity provider

| Entity            | Key                 |
| ----------------- | ------------------- |
| Custom IdP Domain | `domain_properties` |
| Custom IdP User   | `user_properties`   |
| Custom IdP Group  | `group_properties`  |

```json
{
  "custom_property_definition": {
    "domain_properties": null,
    "group_properties": {
      "group_lead": "STRING"
    },
    "user_properties": {
      "birthday": "TIMESTAMP",
      "description": "STRING",
      "last_login": "TIMESTAMP",
      "is_licensed": "BOOLEAN",
      "region": "STRING"
    }
  },
  "name": "My IdP",
  "id": "custom_idp",
  "domains": [
    {
      "name": "domain.biz"
    }
  ],
  "users": [
    {
      "name": "Colby Smith",
      "custom_properties": {
        "is_licensed": false,
        "region": "US-West"
      }
    }
  ]
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/oaa/best-practices/oaa-custom-properties.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
