Primitive CLI
The Primitive CLI (primitive) is a command-line tool for managing your Primitive applications. It handles authentication, app configuration, user management, and provides access to guides and documentation.
Why Use the CLI?
While most day-to-day development happens in your code editor, the CLI is essential for:
AI Agent Integration — The CLI is designed to be used by AI coding assistants (like Claude). When you ask an agent to create a workflow, configure an integration, or deploy a prompt, it uses
primitivecommands to interact with the Primitive server. This enables agents to fully manage your app's backend configuration without you needing to use the admin console.Version-Controlled Configuration — Use
primitive syncto export your app's configuration (prompts, workflows, integrations) as TOML files that live in your repo alongside your code.Automation & CI/CD — Script deployments and configuration changes in your build pipelines.
Quick Admin Tasks — Invite users, check analytics, or test prompts without leaving your terminal.
Agent Usage
If you're working with an AI coding assistant, it can run primitive guides list to see available documentation and primitive guides get <topic> to learn how to use specific features. The agent can then use CLI commands to implement what you've asked for.
Installation
Install the CLI globally via npm:
npm install -g primitive-adminThis installs the primitive command globally on your system.
Authentication
Before using most commands, you need to authenticate:
primitive loginThis opens your browser for OAuth authentication. Tokens are stored per-environment so dev and prod can stay signed in independently — see Project Configuration and Environments below for where they land.
To check your current authentication status:
primitive whoamiTo log out:
primitive logoutProject Configuration and Environments
Primitive CLI config is project-scoped. When you run primitive init or any CLI command inside a directory containing .primitive/config.json (the CLI walks up from the current working directory looking for it), the CLI operates against the named environments declared in that file.
my-app/
├── .primitive/
│ ├── config.json # committed — environment definitions
│ └── credentials.json # gitignored — per-environment tokens
├── src/
└── package.jsonTwo files, two purposes:
.primitive/config.jsonis committable. It defines named environments and an optional default. Your whole team targets the same apps because the file is checked in..primitive/credentials.jsonis gitignored. It holds the per-environment access/refresh tokens written byprimitive login. Never check it in.
Outside a project directory (no .primitive/config.json found by walking up from cwd), the CLI falls back to a user-scope config at ~/.primitive/credentials.json driven by primitive use <app>. Most teams never need this — see User-Scope Fallback below.
Named Environments
A single project usually targets multiple backends — a development app for day-to-day coding, a staging app for QA, a production app for your customers. Each environment in .primitive/config.json binds an apiUrl and an appId together so "which server" and "which app" always travel as one unit:
{
"version": 1,
"defaultEnvironment": "dev",
"environments": {
"dev": { "apiUrl": "http://localhost:8787", "appId": "app_..." },
"prod": { "apiUrl": "https://api.primitive.com", "appId": "app_..." }
}
}Manage environments with the env subcommands:
# Create an environment (auto-creates .primitive/config.json if missing)
primitive env add dev --api-url http://localhost:8787 --app-id app_dev123
primitive env add prod --api-url https://api.primitive.com --app-id app_prod456
# List all environments and inspect one
primitive env list
primitive env show prod
# Set the default environment for this project
primitive env use dev
# Delete an environment (also clears its credential slot, so re-adding the
# same name later cannot reuse stale tokens)
primitive env remove staging--app-id is optional. If you omit it, the environment pins only an apiUrl and the app is chosen per-session via primitive use <appId>.
How the Active Environment Is Resolved
Every command resolves an active environment in this order — the first match wins:
--env <name>flag on the command itselfPRIMITIVE_ENVenvironment variable (handy for CI)defaultEnvironmentin.primitive/config.json(set byprimitive env use)- The only environment defined — if there is exactly one, it's used automatically
- Otherwise the CLI errors and asks you to choose
So in CI you can do:
PRIMITIVE_ENV=prod primitive sync push…or override per-command without changing the default:
primitive workflows runs list --env staging
primitive sync push --env prodSwitching Apps Inside a Project
There is no global "currently active app" in project mode. The active app is whatever the resolved environment's appId says it is. To point an environment at a different app, edit .primitive/config.json (or run primitive env add again to overwrite). primitive use <appId> is a no-op when the resolved environment already pins an appId — for environments that omit appId, use still sets a per-session override.
User-Scope Fallback
For one-off shells with no .primitive/config.json anywhere up the directory tree, the CLI uses a global config at ~/.primitive/credentials.json and an active app set by primitive use:
primitive use "My App"
primitive context # show current contextInside a project directory, prefer primitive env — it's checked in, scoped to the project, and makes it trivial to switch between dev/staging/prod.
Common Commands
Managing Apps
# List all your apps
primitive apps list
# Create a new app
primitive apps create "My New App"
# View app details
primitive apps get
# Update app settings
primitive apps update --mode invite-onlyManaging Users
# List users in your app
primitive users list
# Invite a user
primitive users invite user@example.com
# Remove a user
primitive users remove user@example.com
# List pending invitations
primitive users invitations
# Change a user's role
primitive users set-role <user-id> adminManaging Console Admins
Console admins have access to the admin console for your app. Manage them separately from regular app users:
# List console admins
primitive users admins list
# Add a console admin (sends invitation if they don't have an account)
primitive users admins add admin@example.com
# Remove a console admin
primitive users admins remove <admin-id>
# List pending console admin invitations
primitive users admin-invitations list
# Delete a pending admin invitation
primitive users admin-invitations delete <invitation-id>OAuth Configuration
Configure OAuth providers for your app:
# Set up Google OAuth
primitive apps oauth set-google --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
# Add allowed origins (for CORS)
primitive apps origins add http://localhost:5173
primitive apps origins add https://myapp.comAccessing Guides
The CLI includes built-in guides for various Primitive features:
# List available guides
primitive guides list
# Read a specific guide
primitive guides get documents
primitive guides get workflows
primitive guides get authenticationGuides are cached locally at ~/.primitive/guides/ for offline access.
For AI Agents
When working with an AI coding assistant, point it to these guides before asking it to implement features. For example: "Read primitive guides get workflows and then create a workflow that sends a welcome email when a user signs up." The agent will fetch the guide, learn the patterns, and implement your request correctly.
Configuration Sync
The CLI supports exporting and importing app configuration as TOML files, enabling version control for your app settings.
When using project-scoped environments (set up via primitive env create), the sync directory is resolved automatically as .primitive/sync/<env>/<appId>/ — each environment gets its own isolated slot so a pull --env staging never touches production state:
# Initialize (auto-resolves .primitive/sync/<env>/<appId>/)
primitive sync init
# Pull current configuration from server
primitive sync pull
# See what's changed
primitive sync diff
# Push local changes to server
primitive sync pushPass --dir <path> to override and use a fixed directory (legacy behavior, still supported):
primitive sync init --dir ./config
primitive sync pull --dir ./config
primitive sync push --dir ./configThis creates a directory structure like:
.primitive/sync/<env>/<appId>/
app.toml # App settings
integrations/*.toml # Integration configs
prompts/*.toml # Prompt configs
workflows/*.toml # Workflow definitions
database-types/*.toml # Database type configs + operations
email-templates/*.toml # Email template overridesTIP
If you previously used --dir ./config and are switching to project environments, the CLI will print a migration hint pointing you to the new location. Run primitive sync pull once to re-sync into the new per-environment path.
Claude Code Skill
The CLI includes a built-in skill for Claude Code that gives it expert knowledge of the Primitive platform. When installed, Claude Code automatically follows Primitive patterns and best practices as you build.
# Install or update the skill
primitive skill install
# Check installation status
primitive skill status
# Remove the skill
primitive skill uninstallThe skill is installed to ~/.claude/skills/primitive-platform/SKILL.md. If the skill is already installed, the CLI will automatically update it to the latest bundled version after each command run.
During Init
When you run primitive init, you'll be prompted to install the skill as part of project setup. If you skip it, you can always install it later with primitive skill install.
Advanced Features
Integrations
Configure external API connections:
primitive integrations list
primitive integrations create my-api
primitive integrations test my-apiPrompts
Manage LLM prompts:
primitive prompts list
primitive prompts create my-prompt
primitive prompts test my-promptWorkflows
Build and manage multi-step workflows:
primitive workflows list
primitive workflows publish my-workflow
primitive workflows runs listCron Triggers
Schedule workflows to run on a cron expression:
primitive cron-triggers list
primitive cron-triggers create \
--key nightly-digest \
--workflow send-digest \
--schedule "0 9 * * *" \
--timezone "America/Los_Angeles"
primitive cron-triggers run nightly-digest # fire manually
primitive cron-triggers disable nightly-digest
primitive cron-triggers enable nightly-digest
primitive cron-triggers delete nightly-digestSee Scheduled and Real-Time Automation.
Blob Buckets
Manage general-purpose blob storage:
primitive blob-buckets list
primitive blob-buckets create --key avatars --access-policy authenticated --ttl-tier persistent
primitive blob-buckets blobs avatars
primitive blob-buckets upload avatars ./file.png --content-type image/png
primitive blob-buckets signed-url avatars <blobId> --expires 3600
primitive blob-buckets delete avatars --forceSee Blob Buckets.
Email Templates
Customize the emails Primitive sends for authentication (magic links, OTP codes, etc.):
# List all email types and their override status
primitive email-templates list
# View the current template for an email type
primitive email-templates get magic-link
# Set a custom template (provide subject and/or body files)
primitive email-templates set magic-link --subject "Sign in to MyApp" --html-file ./magic-link.html --text-file ./magic-link.txt
# See available template variables for an email type
primitive email-templates variables magic-link
# Send a test email using the current template
primitive email-templates test magic-link
# Remove a custom override (revert to default)
primitive email-templates delete magic-linkEmail template types include built-in types (magic-link, otp, access-request-created, access-request-resolved, and others) plus any custom kebab-case type names you register. Each type exposes template variables (e.g. , ) that Primitive substitutes at send time. Use primitive email-templates variables <type> to see the full list.
Analytics
View usage metrics:
primitive analytics overview
primitive analytics top-users
primitive analytics user-detail <user-ulid>
primitive analytics user-search --query user@example.com
primitive analytics events --window-days 7
primitive analytics events-grouped --group-by feature
primitive analytics cohort-retention
primitive analytics workflows
primitive analytics prompts
primitive analytics integrationsTest Users for Automated Testing
For integration tests and local dev, Primitive supports a +primitivetest OTP bypass gated by a per-app whitelist. Configure the whitelist with primitive apps update:
# Authorize one or more base emails to derive +primitivetest test accounts
primitive apps update --test-account-bases alice@example.com,bob@example.com
# Inspect the current whitelist (along with other app settings)
primitive apps show
# Clear the whitelist — pass an empty string
primitive apps update --test-account-bases ''The list is capped at 50 base emails per app, and a base cannot itself be a +primitivetest derivative. The whitelist is re-checked on every request, so removing a base immediately revokes its derived sessions.
Each whitelisted base authorizes unlimited derived addresses of the form <base-local>+primitivetest<suffix>@<base-domain>. From the test side, sign in via the normal OTP flow using the magic code 000000:
// In an integration test
await client.otpRequest("alice+primitivetest-teacher@example.com");
await client.otpVerify("alice+primitivetest-teacher@example.com", "000000");
// client is now authenticated; the access token expires in 30 minutesThe derived account must already exist as an AppUser in this app — invite it ahead of time or seed it as part of test setup. The bypass never auto-provisions.
Guardrails:
- Per-app whitelist. Apps without a whitelist have no bypass at all.
- 30-minute tokens with a
primitiveBypass: trueclaim, re-checked per request against the whitelist. - Member scope only.
+primitivetest*cannot hold admin/owner privileges or receive invitations to those roles — boundary calls returnRESERVED_EMAIL_FOR_ADMIN. - AppUser must exist. The derived account has to be an existing member of the app.
Use this for automated tests and local development. See Authentication for the security model in detail.
Scripting
For automation and scripting, most commands support --json output:
primitive apps list --json
primitive users list --jsonTo get the current access token for use with other tools:
primitive tokenGetting Help
Every command has built-in help:
primitive --help
primitive apps --help
primitive users invite --helpNext Steps
- Template App Setup — Create your first app
- Working with Databases — Server-side storage managed via CLI
- Workflows and Prompts — Automation configured via CLI
- Deploying to Production — Deploy your app