Skip to Content
FeaturesCustomers

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, or SUSPENDED

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

  1. Go to Customers in the admin dashboard
  2. Browse the customer list with columns for email, status, last login, and creation date
  3. 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

  1. Add a model with the fields you need (e.g., firstName, companyName, tier) to your foir.config.ts
  2. Set its access to ownerPlane: 'customer' and visibility to private
  3. Run foir push to apply it

See Defining Models → Model Access for the full access shape.

Editing a customer’s record

  1. Open Records for the customer-owned model in the admin
  2. Filter to the customer whose record you want
  3. 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 SUSPENDED to disable accounts without deleting them.
Last updated on