Skip to main content

Custom User Attributes

User attributes are properties that describe your users. Use them to personalize messages, create segments, and analyze your user base.

What are User Attributes?

Attributes are key-value pairs attached to user profiles:

{
"userId": "user_123",
"email": "john@example.com",
"attributes": {
"firstName": "John",
"lastName": "Doe",
"plan": "premium",
"signupDate": "2024-01-15",
"totalPurchases": 5,
"lastLoginDate": "2024-01-20",
"preferences": {
"newsletter": true,
"notifications": "email"
}
}
}

Setting Attributes

Via SDK (Client-Side)

import JoryioSDK from '@joryio/web-sdk';

const joryio = new JoryioSDK({ sdkKey: 'jry_sdk_web_...' });

// Identify a user
joryio.identify('user_123');

// Set attributes separately
joryio.setAttributes({
email: 'john@example.com',
firstName: 'John',
lastName: 'Doe',
plan: 'premium',
signupDate: '2024-01-15'
});

// Update attributes later
joryio.setAttributes({
plan: 'enterprise',
lastUpgrade: new Date().toISOString()
});

Via API (Server-Side)

// Create or update user with attributes
fetch('https://api-eu1.joryio.com/users', {
method: 'POST',
headers: {
'Authorization': 'Bearer jry_live_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: 'user_123',
email: 'john@example.com',
attributes: {
firstName: 'John',
plan: 'premium',
totalOrders: 10,
lifetime Value: 1250.00
}
})
});

Standard vs Custom Attributes

Standard Attributes

Reserved by Joryio (stored at root level):

  • userId - Unique user identifier
  • email - Email address
  • phone - Phone number
  • externalId - External system ID
  • createdAt - User creation timestamp
  • updatedAt - Last update timestamp

Custom Attributes

Any other properties you define (stored in attributes object):

  • firstName, lastName, name
  • plan, subscription Tier
  • company, industry, jobTitle
  • totalPurchases, lifetimeValue
  • preferences, settings
  • And anything else you need!

Data Types

Attributes support multiple data types:

String

{
firstName: "John",
plan: "premium",
country: "US"
}

Number

{
age: 30,
totalPurchases: 15,
lifetimeValue: 1250.50
}

Boolean

{
emailVerified: true,
newsletter: false,
isPremium: true
}

Date/Timestamp

{
signupDate: "2024-01-15",
lastLoginDate: "2024-01-20T10:30:00Z",
trialEndsAt: "2024-02-15T23:59:59Z"
}
Use ISO 8601 Format

For dates, use ISO 8601 format: YYYY-MM-DDTHH:mm:ssZ

Array

{
tags: ["vip", "early-adopter"],
interests: ["technology", "sports", "music"],
purchasedProducts: ["product_1", "product_2"]
}

Object/Nested

{
preferences: {
theme: "dark",
language: "en",
notifications: {
email: true,
sms: false,
push: true
}
},
address: {
street: "123 Main St",
city: "San Francisco",
state: "CA",
zip: "94102"
}
}

Common Attribute Examples

E-commerce

joryio.setAttributes({
// Account info
accountType: "premium",
memberSince: "2024-01-15",

// Purchase history
totalOrders: 15,
lastOrderDate: "2024-01-20",
lifetimeValue: 2500.00,
avgOrderValue: 166.67,

// Preferences
favoriteCategory: "electronics",
preferredShipping: "express",

// Engagement
cartAbandoned: false,
wishlistItems: 5,
reviewsWritten: 3
});

SaaS

joryio.setAttributes({
// Subscription
plan: "pro",
billingCycle: "monthly",
subscriptionStatus: "active",
trialEndsAt: "2024-02-15T23:59:59Z",
mrr: 99,

// Usage
loginCount: 45,
lastLoginDate: "2024-01-20",
featuresUsed: ["export", "api", "integrations"],
apiCallsThisMonth: 10500,
storageUsedGB: 15.5,

// Team
teamSize: 8,
role: "admin",
companyName: "Acme Corp"
});

Media/Content

joryio.setAttributes({
// Subscription
subscriptionTier: "premium",
contentAccessLevel: "unlimited",

// Engagement
articlesRead: 125,
videosWatched: 45,
podcastsListened: 30,
favoriteTopics: ["technology", "business"],

// Behavior
avgSessionDuration: 25.5,
lastVisit: "2024-01-20T14:30:00Z",
deviceType: "mobile"
});

Updating Attributes

Merge vs Replace

Merge (default) - Updates specified fields:

// Initial attributes
{
firstName: "John",
plan: "free",
country: "US"
}

// Update attributes (merges with existing)
joryio.setAttributes({
plan: "premium"
});

// Result (plan updated, others preserved)
{
firstName: "John",
plan: "premium", // Updated
country: "US" // Preserved
}
Attributes are Merged

Calling setAttributes() merges new attributes with existing ones. Existing attributes not mentioned in the call are preserved.

Increment/Decrement

For counters, increment instead of getting current value:

// Bad: Race condition possible
const current = await getUserAttribute('loginCount');
joryio.setAttributes({ loginCount: current + 1 });

// Good: Atomic increment
joryio.incrementAttribute('loginCount', 1);

// Decrement
joryio.incrementAttribute('creditsRemaining', -10);

Array Attributes

You can store and manipulate arrays as attribute values:

// Set array attribute
joryio.setAttributes({
tags: ['vip', 'early-adopter'],
interests: ['technology', 'sports']
});

// Add to array (only adds if value doesn't already exist)
joryio.addToArray('tags', 'premium');
// Result: ['vip', 'early-adopter', 'premium']

// Add duplicate (no-op, prevents duplicates)
joryio.addToArray('tags', 'vip');
// Result: ['vip', 'early-adopter', 'premium'] (unchanged)

// Remove from array
joryio.removeFromArray('tags', 'early-adopter');
// Result: ['vip', 'premium']

Method Details:

MethodDescription
addToArray(key, value)Adds value to array if it doesn't already exist (prevents duplicates). Creates new array if attribute doesn't exist.
removeFromArray(key, value)Removes all instances of value from array. Warns if attribute isn't an array.
Use Cases

Array attributes are perfect for:

  • User tags: ['vip', 'trial', 'beta-tester']
  • Interests: ['sports', 'technology', 'fashion']
  • Purchased products: ['prod_123', 'prod_456']
  • Feature flags: ['feature-a', 'feature-b']
  • Roles: ['admin', 'editor']

Using Attributes for Segmentation

Create segments based on attributes:

Simple Attribute Filter

plan equals "premium"

Numeric Comparison

lifetimeValue >= 1000
totalPurchases > 5
age between 25 and 45

Date-Based Filters

signupDate is within last 30 days
trialEndsAt is within next 7 days
lastLoginDate is more than 14 days ago

String Matching

email contains "@company.com"
country equals "US"
firstName exists
plan is not "free"

Array Filters

tags contains "vip"
interests contains any of ["technology", "business"]

Personalizing Messages

Use attributes in campaign content:

Email Personalization

Hi {{firstName}},

Your {{plan}} plan includes these benefits:
...

{{#if trialEndsAt}}
Your trial ends on {{trialEndsAt}}. Upgrade now!
{{/if}}

Dynamic Content

{{#if plan == "free"}}
<p>Upgrade to Premium for more features!</p>
{{else}}
<p>Thanks for being a {{plan}} member!</p>
{{/if}}

Conditional Blocks

{{#if totalPurchases > 10}}
<div class="vip-offer">
As a valued customer, here's an exclusive offer...
</div>
{{/if}}

Attribute Best Practices

1. Use Descriptive Names

Good:

{
subscriptionTier: "premium",
lifetimeValueUSD: 1250.00,
emailVerified: true
}

Bad:

{
sub: "p",
ltv: 1250,
verified: 1
}

2. Be Consistent

Use the same naming convention:

Good (camelCase):

{
firstName: "John",
lastName: "Doe",
signupDate: "2024-01-15"
}

Bad (mixed):

{
first_name: "John",
LastName: "Doe",
"signup-date": "2024-01-15"
}

3. Use Appropriate Types

Good:

{
age: 30, // Number
isPremium: true, // Boolean
signupDate: "2024-01-15", // ISO Date String
tags: ["vip", "beta"] // Array
}

Bad:

{
age: "30", // String instead of number
isPremium: "true", // String instead of boolean
signupDate: 1705276800, // Timestamp instead of ISO
tags: "vip,beta" // String instead of array
}

4. Don't Store Sensitive Data

Never store:

  • Passwords or password hashes
  • Full credit card numbers
  • Social security numbers
  • Banking details
  • Health information

Safe to store:

  • Last 4 digits of card
  • Payment method type ("visa", "mastercard")
  • Encrypted/hashed tokens
  • Public profile information

5. Keep Attribute Names Short

Good: plan, ltv, mrr Bad: currentSubscriptionPlanTierLevel

Reserved Attribute Names

Avoid using these reserved names:

  • userId, user_id, id
  • email
  • phone
  • createdAt, created_at
  • updatedAt, updated_at
  • $app_id, $platform (prefixed with $)

Bulk Updates

Update attributes for multiple users:

// API: Bulk update - POST /users accepts a bare array body (max 1000)
POST /users
[
{
"userId": "user_1",
"attributes": { "plan": "premium" }
},
{
"userId": "user_2",
"attributes": { "plan": "enterprise" }
}
]

Attribute Limits

LimitValue
Max attributes per user200
Max attribute name length100 characters
Max string value length10,000 characters
Max array length100 items
Max nesting depth5 levels

Troubleshooting

Attributes Not Showing

  1. Check attribute name spelling
  2. Verify user is identified
  3. Enable debug mode to see API calls
  4. Check for API errors in response

Attribute Not Updating

  1. Ensure using merge (not replace)
  2. Check data type matches
  3. Verify attribute name is correct
  4. Check rate limits

Segmentation Not Working

  1. Verify attribute exists for users
  2. Check attribute data type
  3. Test filter logic with known users
  4. Ensure attribute values match filter exactly

Next Steps