Skip to content

Hierarchy

Organization
   └── Project
         └── Prompt
               └── Version (immutable, content-addressed)

Four levels. Each has a specific job. Understanding the boundaries makes everything downstream — release flow, auth model, SDK calls — fall into place.

Organization

A tenant boundary.

  • All users, projects, prompts, and API keys live inside organizations.
  • A user can belong to multiple organizations (Member, Admin, etc. roles).
  • Cross-org reads are not possible. Period.
  • On SaaS, organizations are managed via Clerk. On-prem, they live in your Postgres organizations table and are managed via the on-prem console.

Think: "the company / team / division."

Project

A unit of grouping inside an organization.

  • Belongs to exactly one organization.
  • Has its own members (a user can be a member of the organization but not of every project in it).
  • Project membership has its own roles, including approver — a role that can release versions to environments.

Think: "the application / product / surface." Common patterns:

  • One project per application (customer-app, internal-tools, eval-pipelines)
  • One project per LLM use-case (support-chat, summarizer, classifier)
  • One project per team

There is no right answer — pick the granularity that matches how your team makes prompt-release decisions.

Prompt

A named artifact inside a project.

  • Has a name that's unique within the project (welcome_email, summarizer_v2).
  • Has a kind chosen at creation: instruction, f_string, chat, structured. Kind is set once and not changed afterwards.
  • Has an active version — the SHA of the version that's currently considered "current" for SDK fetches. This is what changes when you "publish" a new version.
  • Has a history of every version ever saved, in reverse chronological order.

A prompt with no active version is invisible to the SDK (returns 404). This is the default state for newly created prompts until you set one.

Why kind is immutable

Each kind has a different content schema. An f_string prompt has a template field; a chat prompt has a messages array. Switching kinds would require rewriting every version's content. If you need a different kind, create a new prompt.

Version

An immutable, content-addressed snapshot of a prompt's content.

  • Created automatically every time you save changes in the UI.
  • Identified by a SHA (a hash of the content) — same content always produces the same SHA.
  • Carries a version label (v1.0, v1.1, etc.) for human readability — labels are display-only; the SHA is the truth.
  • Carries an environments list — which environments this version has been released to.
  • Has comments attached (review threads) and an audit trail.

Versions never change. To "edit" a prompt, you create a new version. The old version remains forever and can be made active again with a single click — that's how rollback works.

welcome_email
├── v1.0  @  sha=8f3a...  released to: [development]                ← active
├── v1.1  @  sha=2d91...  released to: [development, testing]
├── v1.2  @  sha=a7c0...  released to: [development, testing, production]
└── v2.0  @  sha=ff42...  released to: []                            ← draft

Setting v1.2 as active in the example above would immediately make production traffic serve a different prompt without any code change.

Why this structure

A few design choices that look opinionated at first but pay off:

Immutable versions — auditability. You can always answer "what exact prompt ran on this customer's request last Tuesday?" by SHA, even if the prompt has been edited fifty times since.

Active version is a pointer, not a state — instant rollback. Promoting/demoting is swapping a pointer, not a deploy.

Environments are tags on versions, not separate copies — clarity. There aren't four copies of your prompt drifting in parallel. There's one prompt; each version is either released to an env or not.

Project as a release boundary, not the org — projects have their own approvers and permissions, so different surfaces of your product can move at different speeds without needing separate orgs.

Copyright © 2026 elsai foundry.