Customers
Customers represent the end-user accounts on your platform. Foir tracks customer identity, profile data, activity, and segment membership to power personalization and experiments.
Overview
A customer is an end-user account — someone who visits your site, logs in, and interacts with your content. Foir maintains customer records with:
- Identity: email, authentication status, account creation date
- Customer-owned data: custom data you model as a normal model owned by the customer (
access.ownerPlane: 'customer') - Activity: content resolution history, segment memberships, experiment assignments
- Status:
ACTIVE,PENDING, orSUSPENDED
Customers are central to personalization. Segment rules evaluate against customer attributes and behavior, experiments assign customers to variant groups, and targeting rules can check authentication status.
In the Admin
Managing Customers
- Go to Customers in the admin dashboard
- Browse the customer list with columns for email, status, last login, and creation date
- Click a customer to view their detail page
Customer Detail
The customer detail page shows:
- Account information: email, status, email verification, creation date, last login
- Segment memberships: which segments the customer belongs to and when they entered
- Experiment assignments: active experiments and assigned variants
- Activity log: recent content resolution events, including markets, devices, and locales used
- Customer-owned data: records from any customer-owned model (
access.ownerPlane: 'customer') you have defined
Customer-Owned Data
To store custom data on each customer, model it as an ordinary model whose records are owned by the customer who created them. Set the model’s Owner plane to Customer (access.ownerPlane: 'customer'): each customer then owns their own records, isolated from other customers. Mark the model as a singleton to give each customer exactly one record — a profile.
Defining the model
- Add a model with the fields you need (e.g.,
firstName,companyName,tier) to yourfoir.config.ts - Set its access to
ownerPlane: 'customer'and visibility toprivate - Run
foir pushto apply it
See Defining Models → Model Access for the full access shape.
Editing a customer’s record
- Open Records for the customer-owned model in the admin
- Filter to the customer whose record you want
- Create or update their record and save
Customer-owned data is available in segment rules and can be used to drive personalization.
Via the CLI
Customer Management
# List all customers
foir customers list
# Get a customer by ID or email
foir customers get <id>
foir customers get customer@example.com
# Create a customer (email is required; pass extra fields with -d/--data)
foir customers create --email newcustomer@example.com
# Delete a customer
foir customers delete <id>Customer-Owned Data
Customer-owned data is a normal model — its structure lives in foir.config.ts and is applied with foir push, and the records are managed with the standard foir records commands.
Define a customer-owned model in your config:
import { defineConfig, defineModel } from '@foir/cli/configs';
export default defineConfig({
key: 'my-app',
name: 'My App',
models: [
defineModel({
key: 'profile',
name: 'Profile',
fields: [
{ key: 'firstName', label: 'First Name', type: 'text' },
{ key: 'companyName', label: 'Company', type: 'text' },
{ key: 'tier', label: 'Customer Tier', type: 'text', queryable: true },
],
access: {
ownerPlane: 'customer',
defaultVisibility: 'private',
},
}),
],
});# Apply the model definition
foir push
# List and inspect records of the customer-owned model
foir records list profile
foir records get profile <id>
# Create or update a record
foir records create profile --data '{
"firstName": "Jane",
"companyName": "Acme Corp",
"tier": "enterprise"
}'Via the API
Querying Customers
Customer queries require the customers:read scope (SECRET keys). Look up a single customer by id, or list with an optional filter (search matches email; status filters by ACTIVE / PENDING / SUSPENDED).
query GetCustomer {
customer(id: "cust-123") {
id
email
status
createdAt
}
}
query FindCustomers {
customers(filter: { search: "jane@example.com" }, first: 10) {
edges {
node {
id
email
status
}
}
}
}Customer-Owned Data
A customer-owned model is queried through the normal typed record API. The generated type exposes _id plus each field you configured (e.g. firstName, tier) as typed fields. Because the model is owned by the customer plane, a customer can only ever read and write their own records.
For a customer-owned singleton, the authenticated customer reads and writes their one record with the generated my<Model> query and setMy<Model> mutation (customer self-service — no api-key scope). For the profile model above that is myProfile / setMyProfile:
# Customer self-service
query Me {
myProfile {
_id
firstName
tier
}
}
mutation SaveMe {
setMyProfile(input: { firstName: "Jane", tier: "enterprise" }) {
_id
firstName
}
}Operators read customer-owned records with a SECRET key through the standard typed model queries, which return every owner’s records scoped by the key’s permissions:
query Profiles {
profiles(where: { tier: { eq: "enterprise" } }, first: 10) {
edges {
node {
_id
firstName
tier
}
}
}
}Customer-Scoped Records
Content records can be scoped to individual customers, allowing you to store per-customer content such as saved preferences, personalized landing pages, or customer-specific configurations. Records scoped to a customer are only returned when that customer is authenticated.
Best Practices
- Model customer-owned data early — decide what custom data you need on customers, and define a customer-owned model for it, before launch.
- Use email as a natural identifier — the CLI supports looking up customers by email directly.
- Monitor segment membership — check the customer detail page to verify customers land in the expected segments.
- Respect opt-outs — if customers opt out of segments, their preferences are honored regardless of rule evaluation.
- Manage account lifecycle with status — use
SUSPENDEDto disable accounts without deleting them.