# Define attribute keys
> Create custom attribute keys based on any event property to specify the analytical context for attribute calculations. Use built-in keys like domain_userid or define your own.
> Source: https://docs.snowplow.io/docs/signals/attributes/attribute-keys/
> Documentation index: https://docs.snowplow.io/llms.txt

An [attribute key](/docs/signals/concepts/#attribute-keys) is the identifier that attributes are calculated against. All attribute groups need an attribute key.

Signals includes five built-in attribute keys. Four are based on commonly used identifiers from the atomic [user-related fields](/docs/fundamentals/canonical-event/#user-fields) in all Snowplow events. The fifth, `snowplow_id`, calculates attributes per person, using the [Snowplow ID](/docs/identities/concepts/#snowplow-ids) from [Identities](/docs/identities/) when Identities is enabled on your pipeline. See [Calculate attributes per person with identity resolution](/docs/signals/attributes/identity-resolution/).

**Console:**

The built-in attribute keys are available to select when creating an attribute group in Snowplow Console: `domain_userid`, `domain_sessionid`, `network_userid`, `user_id`, and `snowplow_id`. The `snowplow_id` key shows **Requires Identities** until you [configure Identities](/docs/identities/configuration/) on your pipeline.

**Python SDK:**

Import the built-in attribute keys directly:

```python
from snowplow_signals import (
    domain_userid,
    domain_sessionid,
    user_id,
    network_userid,
    snowplow_id,
)
```

***

## Choose a key your application can read by

Your application reads attributes by the same attribute key the attribute group is calculated on, and it reads them on your backend, because reads need an API key. Choose a key whose value your backend has at read time: for example, `domain_sessionid` passed from the browser for session attributes, or `user_id` from an authenticated session for a logged-in user. See [Retrieve attributes](/docs/signals/applications/retrieve-attributes/#handle-the-response).

## Custom attribute keys

You can define custom attribute keys to calculate attributes against identifiers other than the built-in ones. How you specify the key depends on the attribute group [data source](/docs/signals/concepts/#choose-a-data-source).

For stream attribute groups, the key can be any property in your events:

- [Atomic](/docs/fundamentals/canonical-event/#common-fields) properties: fields of the core Snowplow event, available for all events
- Event schema properties: properties within a [self-describing event](/docs/fundamentals/events/#self-describing-events)
- Entity properties: properties from schemas tracked as [entities](/docs/fundamentals/entities/)

For [warehouse attribute groups](/docs/signals/attributes/warehouse-config/), the attributes are pre-calculated in your warehouse table, so the key isn't based on event properties. Instead, define the attribute key using the name of the table column that contains the key values.

**Console:**

Navigate to **Signals** > **Attribute keys** in Console. Click the **Create attribute key** button.

![Create attribute key form with name, description, and property selection](/assets/images/attribute-key-create-7771bedf4e1b02a0915a80001741cef5.png)

You will need to provide:

- A unique name
- An optional description
- An optional email address for the primary owner or maintainer
- Which property you want to calculate attributes against: an atomic field, or a property from an event or entity schema
  - For keys used with warehouse attribute groups, provide the name of the table column that contains the key values instead

To edit or delete a custom attribute key, go to the key details page and click the **Edit** button, or the `⋮` button followed by **Delete**.

**Python SDK:**

Use the `AttributeKey` class to define a custom attribute key. For stream attribute groups, specify the property with the same [property classes](/docs/signals/attributes/attributes/#select-a-property) used for defining attributes: `AtomicProperty`, `EventProperty`, or `EntityProperty`.

```python
from snowplow_signals import AttributeKey, AtomicProperty

app_id_attribute_key = AttributeKey(
    name="app_id_attribute_key",
    description="The id for the app",
    property=AtomicProperty(name="app_id"),
)
```

This table lists all available arguments:

| Argument          | Description                                                                                     | Type                                                   | Required?                              |
| ----------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------- |
| `property`        | The property to calculate attributes against, for stream attribute groups                       | `AtomicProperty`, `EventProperty`, or `EntityProperty` | Either `property` or `external_column` |
| `external_column` | The column in your warehouse table that contains the key values, for warehouse attribute groups | `string`                                               | Either `property` or `external_column` |
| `name`            | The name of the attribute key (derived from `property` or `external_column` if not set)         | `string`                                               | ❌                                      |
| `description`     | A description of the attribute key                                                              | `string`                                               | ❌                                      |
| `owner`           | The owner of the attribute key                                                                  | `string`                                               | ❌                                      |
| `ttl`             | Time attributes for this key will live in the Profiles Store                                    | `timedelta`                                            | ❌                                      |

> **Note:** Earlier versions of the SDK used a `key` argument that accepted atomic properties only. It's deprecated: use `property` instead.

Here's a full example using a property from a custom `user` entity:

```python
from datetime import timedelta
from snowplow_signals import AttributeKey, EntityProperty

customer_tier_attribute_key = AttributeKey(
    name="customer_tier_attribute_key",
    description="Attribute key for analyzing behavior by customer tier",
    property=EntityProperty(
        vendor="com.example",
        name="user",
        major_version=1,
        path="tier",
    ),
    owner="analytics-team@company.com",
    ttl=timedelta(days=365),
)
```

For [warehouse attribute groups](/docs/signals/attributes/warehouse-config/), use `external_column` to define the key from the table column that contains the key values:

```python
from snowplow_signals import AttributeKey

customer_id_attribute_key = AttributeKey(
    external_column="CUSTOMER_ID",
    description="Attribute key for the customer ID column in the transactions table",
)
```

The key's `name` is set to the column name automatically. If you set both, they must match.

Custom attribute keys are used in attribute groups in the same way as built-in keys.

***
