Next Zitadel preview: Your user model, not ours

When we shipped the first Next Zitadel preview, we said npx @zitadel/cli@alpha setup was only the first step. The bigger direction was making identity configuration feel like part of the application workflow, starting with the shape of a user itself. Today, that starts landing with user schemas.

Most identity systems ship with a predefined user model—an email, a name, maybe a phone number—and expect your application to adapt to it. We think it should work the other way around: identity should adapt to your application, not force your application to adapt to your identity provider.

If your users are customers, employees, merchants, drivers, devices, or agents acting on someone's behalf, your identity system should model those users instead of forcing you to build around a predefined user object.

A user schema is a JSON Schema document describing what a user in your application looks like. You write it, version it, and review it like the rest of your code. Every project starts with a default human user schema like this:

{
  "kind": "user-schema",
  "objectType": "human-user",
  "title": "DefaultHumanUserSchema",
  "metaSchema": "${SERVER_URL}/user-schema.json",
  "type": "object",
  "x-auth-methods": {
    "password": { "enabled": true, "position": 1 },
    "passkey": { "enabled": true, "position": 2 }
  },
  "required": ["email"],
  "properties": {
    "email": { "type": "string", "format": "email", "x-unique": "project" },
    "givenName": { "type": "string", "maxLength": 50 },
    "familyName": { "type": "string", "maxLength": 50 },
    "dateOfBirth": { "type": "string", "format": "date" }
  }
}

Every project starts with a default human user schema, so you can sign up and log in without writing one yourself. It's just the starting point: edit or replace it with your own schema in .zitadel/schemas, run apply, and Zitadel validates every user against it and stores whatever properties you define.

Adding a new attribute doesn't require a database migration or a second table for custom fields. It’s all JSON Schema: standard keywords for standard validation, x- annotations for identity-specific behavior, like x-unique controls uniqueness, x-sensitive keeps fields out of audit logs, and so on.

Model the user types your application needs

Not every application has the same kind of users. A customer needs an email address and a recovery path. A service account doesn't need a profile at all, just credentials. An AI agent might only need a reference to the user it acts on behalf of.

Each of those is just a JSON Schema document. Human, machine, agent, or whatever your domain requires.

User schemas describe attributes, not credentials. Authentication methods are configured separately using x-auth-methods at the schema root, so different user types can support different ways of signing in. For example, a service account can enable API keys while disabling passwords entirely.

Because the schema is the single source of truth for user attributes, changing a field's validation updates every place that field is used. There's no separate form definition or validation logic to keep in sync.

What's included in this user schema preview

  • As many user schemas as your application needs, each with exactly the fields it requires. Your user model, not ours.
  • Invalid schemas are rejected before they're applied, so mistakes never reach your users.
  • No database migrations. Add a property, run apply, and Zitadel immediately validates and stores it.
  • plan and apply. Preview exactly what will change before publishing it to the server.

.zitadel/: your config, checked into your repo

If you tried the previous preview, start with a fresh project. The CLI and project structure have evolved, so rerunning setup on an existing project won't pick up the new workflow.
Once set up, you get a ./zitadel folder next to your app code, checked out locally so you can edit it and put it in a pull request like anything else in your repo:

.zitadel/
  schemas/
    default-human-user.json
  flows/
    default-login.json

.zitadel/schemas/ holds one JSON file per user type. Every project starts with default-human-user.json, and you can add additional schemas for whatever your application needs. Future previews will build on this foundation with additional user types, including AI agents.

A new schema property isn't visible to users on its own. Adding a field to the schema means it can be validated and stored, but registration only asks for it once you also list it under the register step's fields in .zitadel/flows/default-login.json. Flows are their own topic for a future post; the one thing worth knowing now is that the schema defines what data exists, and a flow step decides when it's collected.

An example: adding a team field

Say you want every human user to belong to a team. Open .zitadel/schemas/default-human-user.json and add the property:

   "properties": {
     "email": { "type": "string", "format": "email", "x-unique": "project" },
     "givenName": { "type": "string", "maxLength": 50 },
-    "familyName": { "type": "string", "maxLength": 50 }
+    "familyName": { "type": "string", "maxLength": 50 },
+    "team": { "type": "string", "maxLength": 50 }
   },
-  "required": ["email"]
+  "required": ["email", "team"]

That alone doesn't put a field on your registration form. The sign up flow doesn't know how to ask for it yet. Open .zitadel/flows/default-login.json, find the register step, and add team to its fields array, the same way it already lists givenName and familyName.

Then preview and confirm the change. plan compares your local .zitadel/ folder with what's currently on the server and shows exactly what would change — the new field, the updated required fields, and the flow changes — before anything is applied.

npx @zitadel/cli@alpha plan

apply publishes exactly what plan showed you. From that point on, every new user is validated against the updated schema, and the registration flow collects the new team field.

npx @zitadel/cli@alpha apply

What user schemas unblock next: flows as code and agent identities

User schemas are the foundation for what's coming next. Registration and login flows become configuration instead of hardcoded forms, and AI agents become just another user type with their own schema and authentication methods.

Try it with a coding agent

If you're driving it through a coding agent, use this prompt as-is:

Add a required `team` attribute to `.zitadel/schemas/default-human-user.json`. Use the same `maxLength` as the existing string fields.

Then update `.zitadel/flows/default-login.json` so the `register` step collects the `team` field during sign-up.

Run `npx @zitadel/cli@alpha plan` and show me the proposed changes. Don't make any changes on the server yet. Only run `npx @zitadel/cli@alpha apply` after I've reviewed and approved the plan.

We want your feedback

We'd love you to try the full workflow:

  • Set up a new project.
  • Edit the generated user schema.
  • Update the registration flow to collect your new fields.
  • Run plan to review the changes.
  • Run apply to publish them.
  • Register a user and verify the experience matches your configuration.

As you go, we'd especially like to know:

  • Was it obvious where to make changes?
  • Was the relationship between schemas and flows clear?
  • Did plan give you confidence about what would happen before applying?
  • Were validation errors easy to understand and act on?
  • What parts of the workflow felt confusing or cumbersome?

We'd love to hear your feedback in GitHub Discussions or on Discord.

Liked it? Share it!