Skip to main content

Overview

The User API is a persistent, per-user key-value store that survives across conversations and sessions. Use it to store any data tied to a user β€” onboarding progress, workflow state, preferences, cart contents, verification status, or any custom fields your agent needs. Think of it as a schemaless user database: read and write any property, and it persists automatically. This makes it ideal for multi-step flows where your agent needs to remember where a user left off.

Read-Only User Profile (_luaProfile)

A new, read-only property user._luaProfile is now available on the user object. This provides a secure and reliable way to access core user identity information.
  • _luaProfile (Read-Only): Contains essential user data like userId, fullName, mobileNumbers, and emailAddresses.
    • The _lua prefix indicates this is a special, system-provided property.
    • This data is immutable; any attempts to change it will be silently ignored.
  • user.* (Mutable): Continue to use the main user object to store and manage any custom data your agent needs, such as preferences, shopping carts, or game scores.

Core User Identity

Access read-only data like user._luaProfile.userId and user._luaProfile.fullName.

Persistent Storage

Store any data on the user object β€” onboarding state, workflow progress, preferences, cart contents. Persists across all conversations and sessions.

Deprecated user.userId

To centralize core user information, user.userId is now deprecated. Please update your code to use user._luaProfile.userId. The old property will be removed in a future version.

User as a State Store

The User object is not just for profile data β€” it is a persistent state store for building multi-step, stateful agent workflows. Any property you write to the user object persists across conversations, sessions, and even days or months.
Key insight for AI agents and developers: The User object is the primary way to maintain state across conversations. Use it to track onboarding progress, accumulate data across tool calls, and resume workflows exactly where the user left off.

Onboarding State Machine Example

Common State Storage Patterns

Features

Direct Access

Access properties with user.name instead of user.data.name

Auto Sanitization

Removes sensitive fields automatically

Built-in Methods

update(), save(), send(), and clear() included

Messaging

Send text, images, and files to users

get(identifier?)

Retrieve user data as a UserDataInstance. Supports lookup by userId, email, or phone number.
string | UserLookupOptions
One of:
  • No parameter: Returns current user from conversation context
  • string (userId): Retrieve a specific user by ID
  • { email: string }: Look up user by email address
  • { phone: string }: Look up user by phone number (with or without + prefix)
Required in: Webhooks, pre-defined LuaJob (no conversational context)Optional in: Tools, dynamic jobs (has conversational context)
Returns: UserDataInstance with proxy-based property access, or null if user not found (for email/phone lookup)
Look up users by email or phone β€” especially useful in webhooks where you receive contact info from external systems but don’t have the internal userId.

Shortcut: User.getChatHistory()

If you only need chat history for the current user (in a tool with conversational context), the top-level static User.getChatHistory() skips the User.get() step:
This is equivalent to (await User.get()).getChatHistory() β€” see the instance method below for the full return shape and examples.

When to Use userId Parameter

Understanding when userId is required vs optional:
Context Matters:
  • Tools: identifier is optional - defaults to current user in conversation
  • Webhooks: identifier is REQUIRED - use userId, email, or phone lookup
  • LuaJob (pre-defined): identifier is REQUIRED - use userId, email, or phone lookup
  • Jobs API (dynamic): Use jobInstance.user() instead - automatic context captured!
New! In webhooks, you can now look up users by email or phone if you don’t have the userId:
Examples:
Get the current user from conversation context:

UserDataInstance API

Property Access (Direct)

Access any user property directly:

update()

Update user data on the server and locally.
object
required
Object containing fields to update or add
Returns: Promise resolving to updated sanitized user data Examples:

save()

Save the current state of user data to the server. This is a convenience method that persists all changes made to the user instance.
Returns: Promise resolving to true if successful Examples:
Tip: The save() method provides a simpler workflow - modify properties then save, rather than passing data to update().

send()

Send messages to the user conversation. Supports text, images, and file attachments.
Message[]
required
Array of messages to send (text, image, or file)
Message Types:
Examples:
user.send() vs Channels.send: user.send() delivers to the user on the channel they’re already active on β€” simplest when you have a User and just want to reach them. Use Channels.send when you need to choose a specific channel (e.g. always WhatsApp), reach a cold phone number or email with no prior conversation, or send an approved WhatsApp template. Both record the message to the user’s conversation thread. See Proactive Messaging.

getChatHistory()

Retrieve the conversation history for the current user with the active agent. Returns the last 40 user/assistant messages, transformed for display: hidden text is filtered out and embedded media (audio, video, files) is surfaced as structured content parts you can render directly.
Return shape:
Example:

clear()

Clear all user data for the current user.
Returns: Promise resolving to true if successful Example:
Destructive operation! This removes all user data. Use with caution.

Data Sanitization

UserDataInstance separates system identity from custom data: System identity (read-only via _luaProfile):
  • userId, fullName, mobileNumbers, emailAddresses
  • Extracted from response and made immutable β€” access via user._luaProfile
Custom data (read/write via direct properties):
  • name, email, phone
  • Custom fields you set
  • Preferences, settings, and any other data

Complete Examples

Example 1: User Preferences

Example 2: Shopping Cart Persistence

Example 3: User Profile Management

Example 4: Personalized Greeting

Example 5: Order Notification with Messaging

Example 6: Send Receipt with Image

Best Practices

Combine multiple updates into one call:
Not all fields may be present:
The new save() method is perfect for multiple changes:
Send messages to users for order updates, alerts, and more:
Ensure correct message format:

TypeScript Support

Usage with Types

If user properties aren’t what you expect, log the user object to see what’s actually stored:
Then run lua logs --type skill --limit 5 after a test message. See the Debugging Skills guide for the full workflow.

Next Steps

Data API

Store custom user data

User Data Examples

See working examples

Debugging Skills

Inspect runtime return values