01Why build this

The single biggest change in pharma promotional-material work over the last 18 months: the first draft is now written by AI. ChatGPT, Claude, Gemini, in-house LLMs — whichever stack — MR-facing materials, HCP explanations, patient IP, internal training material all increasingly start from an AI prompt.

Three inefficiencies have piled up across the industry, however.

The conclusion up front. Prompts are "structural thinking", not company secrets. The output (the actual material) is still reviewed under each company's own responsibility, so opening the prompts does not lower compliance. It lifts compliance sensitivity across the whole industry. This proposal is for a system that operates prompt openness and continuous improvement as a shared public good for the industry.

Analogy: medical papers publish results without dictating any reader's clinical decisions, yet the accumulation of papers lifts the quality of judgment industry-wide. Prompt Commons brings the same structure to the world of AI prompts.

02What we are building

A web platform with six elements.

Element 01

Prompt catalog

Searchable, taggable, browsable catalog of industry-shared prompts. Each prompt written in Markdown.

Element 02

Version control

Each prompt has a Git-like history. Who changed what, when, traceable. Forkable.

Element 03

Discussion threads

A discussion thread on each version. Compliance feedback, improvement proposals, field reports meet here.

Element 04

Rating system

Votes, reactions, adoption reports. Community collective intelligence selects quality.

Element 05

Moderation

Automatic scan for clear violations (Pharmaceutical Act §66/§68, etc.) plus human flags. Policy detailed below.

Element 06

AI improvement

(Later phase) The AI learns from discussions and generates improvement proposals. A distillation of industry knowledge.

These elements together form a continuous, industry-wide discussion cycle started from a prompt.

03Why it can be opened

The first objection: "We won't share ours. It's competitive advantage." Three-step answer.

Step 1 — where the advantage actually lives: a pharma company's competitive advantage lives not in the prompt itself but in (a) clinical data, (b) understanding of the target patient, (c) MR field knowledge, (d) internal review for output materials. The prompt is just the vessel that expresses these. Sharing the vessel does not share what's inside.

Step 2 — gains from opening: a company that publishes its prompt gets feedback from the entire industry. Compliance angles, expression improvements, unseen risks. One head works inside one company; a hundred heads work on a published prompt.

Step 3 — managing the risk of opening: confidential parts can be abstracted with variables like {{PRODUCT_X}}. The platform's guidelines forbid embedding specific pre-approval information, trial data, or commercial secrets. Automatic pre-publication scans catch lapses.

Core idea: the "thinking" of the prompt becomes shared public good for the industry, while the "content" remains each company's responsibility to safeguard. This separation is the logic that makes opening possible.

04Use scenarios

Scenario A — veteran reviewer publishes their own prompt

Y, a veteran at pharma X, publishes "a prompt that structures MR-facing explanations without implying off-label use." Y has three years of internal use without a §66 violation flag. On publication, Y attaches an anonymized operational log summary.

Scenario B — a newcomer learns from an industry-shared prompt

W, a newcomer at pharma Z, has no company prompt of their own. On Prompt Commons they compare 12 prompts under the "MR foundational material" tag, read the top three by vote, and follow the discussion threads. The discussion threads become the textbook.

Scenario C — a compliance lead feeds back industry discipline

V, a JPMA Code interpretation lead, comments on the discussion thread of prompt P-218: "The expression here conflicts with the latest interpretation of JPMA Code §3.1." The original author and other users discuss; a forked improvement, P-218.v2, appears. Industry discipline propagates not as a document but as a prompt diff.

Scenario D — an AI developer maps industry needs

U, a developer at an LLM provider, follows discussions tagged "Pharmaceutical Act compliant" and "Sales-Info Guideline compliant" to see what the industry wants from AI and where it is stuck. The next-generation model's fine-tuning direction tracks industry need.

05Core features

Functions organized by role.

AreaCapabilitiesRole
CatalogSearch, tag filter, sort (popular / new / active discussion), related-prompt recommendationEveryone
AuthoringNew prompt, edit, Markdown preview, variable definition, sample-output attachmentAuthor
VersioningEdit history, diff view, branch (fork), merge proposal (PR-equivalent)Author, editor
DiscussionThreaded comments, Markdown support, quoting, reactions (👍 / ⚠️ / 💡)Everyone
RatingVote (+/-), adoption report ("in production", "piloting", "modified", "declined"), reputationEveryone
ComplianceAutomatic scan (§66/§68 pattern detection), community flag, moderator action, removal logModerator, all
NotificationsWatch, email / web push, new comment / new versionEveryone
UserPseudonym account, optional org disclosure, COI declaration, reputation historyEveryone
APIREST + WebSocket, external tool integration, data exportDeveloper

06Architecture

When picking the tools (the technology), three things came first: (a) it can run on the server we already rent (a VPS, meaning a single machine rented for our own use), (b) we can start small and add capacity later, (c) the monthly running cost stays low.

What follows is a diagram split into three layers — the screen, the processing, and the data storage — and the tools used in each. Some of the names will be unfamiliar, but each comes with a one-line note on what it is for. If a name is hard to place, skip it; picking up the role is enough.

Overall

┌─────────────────────────────────────────────────────────────┐
│  Frontend (Next.js 14 App Router, TypeScript, Tailwind)     │
│  - SSR for SEO + Client interactivity                       │
│  - Markdown editor (CodeMirror 6)                           │
│  - Diff viewer (react-diff-viewer-continued)                │
└──────────────────┬──────────────────────────────────────────┘
                   │ HTTPS / WebSocket
┌──────────────────┴──────────────────────────────────────────┐
│  API (FastAPI + Pydantic, Python 3.12)                      │
│  - REST endpoints                                           │
│  - WebSocket (real-time comments)                           │
│  - Background jobs (Celery + Redis)                         │
└──────────────────┬──────────────────────────────────────────┘┌──────────────────┴──────────────────────────────────────────┐
│  Storage Layer                                              │
│  - PostgreSQL 16 (primary data)                             │
│  - Redis 7 (cache, sessions, job queue)                     │
│  - S3-compatible object store (attachments, exports)        │
│  - Meilisearch (full-text search; Phase 2)                  │
└─────────────────────────────────────────────────────────────┘

Compliance Scan Service (separate microservice)
  - Pattern matching (regex + rules)
  - LLM-based review (Phase 3+)
Notification Service
  - Email (SES / Resend)
  - Web Push (VAPID)
Auth: NextAuth + Email OTP / OIDC (pseudonym supported)

A word on the less familiar names in the diagram. WebSocket keeps a line open between the server and the browser so that a new comment reaches the screen the instant it is posted (ordinary traffic only answers when asked; this one speaks up on its own). Redis is a small fast holding area that keeps frequently used data close at hand for quick retrieval. Celery is the worker that handles slow jobs (sending email, running automatic checks) in the background. None of these are visible to the user; they are the stagehands that keep the display fast and the waiting short.

Hosting

Why these choices

ChoiceWhy
Next.jsThe tool for building the screen. It assembles the page on the server first (this is SSR) and sends it, so search engines can read the content easily. Once it arrives, it comes to life in the browser (this is hydration, the step that adds interactive behavior to a still page), so buttons and inputs feel smooth. Since this is a place meant to be found through search, that fit matters.
FastAPIThe tool for building the counter (the API) that connects the screen and the data. Deciding the "shape" of the data up front cuts mistakes, and a spec sheet (a list of what counters exist) is generated automatically.
PostgreSQLThe box that stores the data (the database). Prompts, their versions, and comments link together in tangled ways, so we chose one that is good at such linking. It also has soft-shaped data storage (JSONB) and whole-text searching (full-text search) built in, which is plenty at launch scale.
MeilisearchA search-only tool. It can split a Japanese sentence into words to look things up, so it is added in a later stage (Phase 2).
MarkdownA light way of writing using only simple marks. It is already widely used for writing prompts and pairs well with change-history tracking.

07Data model (DB schema)

This is the design for which "tables" the data gets split into. There are eleven main tables, placed on top of the storage box mentioned earlier (PostgreSQL 16). The code below is the instruction sheet for creating those tables (written in SQL, the way you give commands to a database). If you are not an engineer, you do not have to read the code itself. Picking up the comments on the right of each line (the users table, the prompt-body table, the discussion table, and so on) is enough to grasp the overall picture of what gets stored and how.

-- Users and organizations --
CREATE TABLE users (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pseudonym     TEXT NOT NULL UNIQUE,           -- public name (pseudonym OK)
  email_hash    TEXT NOT NULL UNIQUE,           -- SHA-256 of email (login key)
  display_name  TEXT,                              -- optional real name
  org_id        UUID REFERENCES orgs(id),       -- optional org disclosure
  coi_text      TEXT,                              -- COI declaration
  reputation    INTEGER NOT NULL DEFAULT 0,
  role          TEXT NOT NULL DEFAULT 'member', -- member / mod / admin
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  status        TEXT NOT NULL DEFAULT 'active'   -- active / suspended / banned
);

CREATE TABLE orgs (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name          TEXT NOT NULL UNIQUE,
  type          TEXT,                              -- pharma / consulting / academic / regulator
  verified      BOOLEAN NOT NULL DEFAULT FALSE,
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Prompt --
CREATE TABLE prompts (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  slug                TEXT NOT NULL UNIQUE,
  title               TEXT NOT NULL,
  summary             TEXT,
  current_version_id  UUID,
  owner_user_id       UUID NOT NULL REFERENCES users(id),
  fork_of_prompt_id   UUID REFERENCES prompts(id),
  license             TEXT NOT NULL DEFAULT 'CC-BY-4.0',
  status              TEXT NOT NULL DEFAULT 'published',
  view_count          INTEGER NOT NULL DEFAULT 0,
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_prompts_owner ON prompts(owner_user_id);
CREATE INDEX idx_prompts_status ON prompts(status) WHERE status = 'published';

CREATE TABLE prompt_versions (
  id                   UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  prompt_id            UUID NOT NULL REFERENCES prompts(id),
  version_label        TEXT NOT NULL,
  content_md           TEXT NOT NULL,
  changelog            TEXT,
  parent_version_id    UUID REFERENCES prompt_versions(id),
  author_user_id       UUID NOT NULL REFERENCES users(id),
  sample_output_md     TEXT,
  variables_json       JSONB,
  created_at           TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(prompt_id, version_label)
);
CREATE INDEX idx_versions_prompt ON prompt_versions(prompt_id);

-- Discussion --
CREATE TABLE discussions (
  id                UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  prompt_version_id UUID NOT NULL REFERENCES prompt_versions(id),
  title             TEXT NOT NULL,
  opened_by_user_id UUID NOT NULL REFERENCES users(id),
  status            TEXT NOT NULL DEFAULT 'open',
  category          TEXT,                          -- compliance / improvement / question / report
  comment_count     INTEGER NOT NULL DEFAULT 0,
  created_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at        TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE comments (
  id                UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  discussion_id     UUID NOT NULL REFERENCES discussions(id),
  parent_comment_id UUID REFERENCES comments(id),
  author_user_id    UUID NOT NULL REFERENCES users(id),
  content_md        TEXT NOT NULL,
  status            TEXT NOT NULL DEFAULT 'visible',
  created_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  edited_at         TIMESTAMPTZ
);
CREATE INDEX idx_comments_discussion ON comments(discussion_id);

-- Rating --
CREATE TABLE votes (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  target_type   TEXT NOT NULL,
  target_id     UUID NOT NULL,
  user_id       UUID NOT NULL REFERENCES users(id),
  value         SMALLINT NOT NULL CHECK (value IN (-1, 1)),
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(target_type, target_id, user_id)
);

CREATE TABLE reactions (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  target_type   TEXT NOT NULL,
  target_id     UUID NOT NULL,
  user_id       UUID NOT NULL REFERENCES users(id),
  emoji         TEXT NOT NULL,
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(target_type, target_id, user_id, emoji)
);

CREATE TABLE adoptions (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  prompt_id     UUID NOT NULL REFERENCES prompts(id),
  user_id       UUID NOT NULL REFERENCES users(id),
  status        TEXT NOT NULL,             -- 'in_production' / 'piloting' / 'modified' / 'declined'
  notes         TEXT,
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Tags --
CREATE TABLE tags (
  id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name    TEXT NOT NULL UNIQUE,
  type    TEXT NOT NULL                   -- 'topic' / 'regulation' / 'material_type' / 'language'
);

CREATE TABLE prompt_tags (
  prompt_id  UUID NOT NULL REFERENCES prompts(id),
  tag_id     UUID NOT NULL REFERENCES tags(id),
  PRIMARY KEY(prompt_id, tag_id)
);

-- Moderation --
CREATE TABLE moderation_flags (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  target_type     TEXT NOT NULL,
  target_id       UUID NOT NULL,
  reporter_user_id UUID REFERENCES users(id), -- NULL when from auto-scan
  reason_code     TEXT NOT NULL,
  reason_text     TEXT,
  status          TEXT NOT NULL DEFAULT 'open',
  resolved_by_user_id UUID REFERENCES users(id),
  resolution_notes TEXT,
  created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  resolved_at     TIMESTAMPTZ
);

-- Audit log (append-only) --
CREATE TABLE audit_log (
  id          BIGSERIAL PRIMARY KEY,
  actor_user_id UUID REFERENCES users(id),
  action      TEXT NOT NULL,
  target_type TEXT NOT NULL,
  target_id   UUID NOT NULL,
  payload     JSONB,
  ip_hash     TEXT,
  user_agent  TEXT,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_audit_target ON audit_log(target_type, target_id);

Key invariants

08API design

The counter through which outside tools and apps exchange data with this service is called an "API". Two kinds are provided here. One is REST, a very ordinary set of manners for the exchange (it fixes "go to this address and this data comes back"). The other is the WebSocket mentioned earlier, used to deliver new discussion comments on the spot. The list of counters (which address returns what) is written out automatically as a spec sheet (in a shared format called OpenAPI 3.1).

Endpoints (REST)

Prompts

GET /api/v1/prompts List. Query: ?tag=, ?sort=, ?page=, ?q= (full text)
GET /api/v1/prompts/:slug Prompt detail + latest version
POST /api/v1/prompts Create (auth). body: title, summary, content_md, tags[]
POST /api/v1/prompts/:slug/fork Fork: create a new prompt with fork_of_prompt_id set
PUT /api/v1/prompts/:slug Metadata update (owner only)
DELETE /api/v1/prompts/:slug Archive (owner or moderator)

Versions

GET /api/v1/prompts/:slug/versions All versions (newest first)
GET /api/v1/prompts/:slug/versions/:label Get a specific version
POST /api/v1/prompts/:slug/versions Submit a new version (open to anyone)
GET /api/v1/prompts/:slug/diff?from=v1&to=v2 Diff between versions

Discussion

GET /api/v1/prompts/:slug/discussions All discussions on a prompt
POST /api/v1/prompts/:slug/discussions Open a new discussion
GET /api/v1/discussions/:id/comments Get the comment tree
POST /api/v1/discussions/:id/comments Post a comment. body: content_md, parent_comment_id?

Rating

POST /api/v1/votes Vote. body: target_type, target_id, value (+1/-1)
POST /api/v1/reactions Add a reaction
POST /api/v1/adoptions Report adoption

Moderation

POST /api/v1/flags Flag content (anyone)
GET /api/v1/moderation/queue Moderator queue (requires role)
PUT /api/v1/moderation/flags/:id Resolve a flag (requires role)

WebSocket (real time)

# Connect
wss://prompt-commons.example.com/ws

# Subscribe (when a discussion page opens)
{
  "type": "subscribe",
  "channel": "discussion:<discussion_id>"
}

# Pushed events
{ "type": "comment.created", "data": {...} }
{ "type": "comment.edited",  "data": {...} }
{ "type": "reaction.added", "data": {...} }
{ "type": "version.published", "data": {...} }

Auth flow

  1. Sign-up: email → OTP → pick a pseudonym → done
  2. Login: email → OTP → session cookie (HttpOnly, Secure, SameSite=Lax)
  3. API auth: session cookie (web) or JWT bearer (external tools)
  4. Org verification: optional; auto-verified via org-domain email

Rate limits

ClassAnonLogged in
GET (reads)60 req/min300 req/min
POST (authoring)10 req/min, 100 req/day
POST (rating)60 req/min
POST flag5 req/hour

09Moderation

Three layers: automatic → community → moderator. Machines catch the clearly violative, the community surfaces the ambiguous, moderators decide the close calls.

Layer 1 — automatic scan (pre-publication)

On new prompt or comment, run:

Behavior on hit:

Layer 2 — community flag

Anyone can flag content via POST /flags. Reasons:

CodeMeaningHandling
pharma_act_66Pharmaceutical Act §66 (exaggeration)To moderator queue
pharma_act_68§68 (pre-approval advertising)Immediate moderator queue + temporary unpublish
jpma_codeJPMA Code concernQueue + redirect to discussion
off_topicUnrelated to pharma promoQueue
spamSpamAuto-hide after 3 flags; moderator review
personal_dataPII embeddedImmediate mask + moderator alert
otherOtherQueue

Layer 3 — moderator decisions

Moderators may:

Principle: every moderator action is logged in audit_log. Monthly statistics (removal counts, flag counts, breakdown) are published.

Moderator selection

10Governance & license

License

Default license: CC BY 4.0 (free use with attribution). Authors may choose CC0 (public domain) instead. Commercial use is allowed under both — consistent with the "industry shared good" concept.

Operating body

Initially run by Advertising Regulation in Japan (Promotional Material Review) and Artificial Intelligence (this site's editorial). Phase 2+ moves to a cross-industry steering committee:

COI policy

Privacy & data protection

11Phased rollout

Launch in stages; bring in real-use feedback at every stage.

Phase 01–2 weeks

MVP — static catalog + discussions on existing Mattermost

Stand up a sub-section /prompt-commons/catalog/ inside this very site. Prompts as Markdown files (Git). Discussions link out to an existing Mattermost channel. Zero spend, used to validate demand.

  • Catalog 10 seed prompts
  • Each prompt links to its Mattermost thread
  • Adoption reports collected via Google Form
Phase 11 month

Basic features — web authoring + version control

Minimum Next.js + FastAPI + PostgreSQL stack stood up. Authoring, versions, and search work.

  • Auth (OTP), profiles
  • Prompt CRUD, tags
  • Version control + diff view
  • Simple full-text search (Postgres FTS)
  • Ops: existing VPS + Docker
Phase 22 months

Discussion + community

Discussion threads, votes, reactions, adoption reports, community flags.

  • Threaded comments with Markdown
  • Vote / Reactions / Adoptions
  • WebSocket for real-time
  • Notifications (email + web push)
  • Community flag + moderator dashboard
  • Meilisearch (JA morphological analysis)
Phase 33 months

Compliance automation + AI improvement

Production-grade auto-scan (rule + LLM). Discussion-aware AI improvement suggestions.

  • Structured rule DB (§66/§68, Sales-Info Guideline, JPMA Code)
  • Two-stage scan (regex + LLM) on submission
  • Discussion summarizer (long-thread digest)
  • Similar-prompt recommendation (embeddings)
  • "Suggest an improvement to this prompt"
Phase 46 months

Fine-tune integration and distillation

Approved high-quality prompts + discussion logs feed an industry-specialized LLM. Hosted LLM API offered as a (paid) option to interested companies.

  • Annotate the high-quality corpus
  • Domain-adapt a base LLM (e.g., Llama 4)
  • Dedicated API gateway
  • Publish benchmarks (§66 compliance rate, expression quality)
  • Monetization: paid hosted API — funds operations

12Risks & responses

RiskImpactResponse
Confidential leakHighPre-publication auto-scan (PII, trial data, pre-approval info) + clear guidelines + recommended variable templating
Regulatory-violating promptsHighAuto-scan + community flag + moderator. Clear breaches: immediate unpublish. Ambiguous: visible discussion
Conformity pressure (one prompt becomes "the answer")MediumVote labeled as guidance not verdict; minority views surfaced; fork encouraged
Misuse by competitorsLowOpen by design; "misuse" concept is weakened. License keeps attribution
Continuity of operationsMediumMove to cross-industry committee at Phase 2+. Portable data (Markdown + JSON export)
Failure to acquire initial usersHighPhase 0: 10 high-quality seed prompts authored by editorial. Decision gate: 50 posts in 3 months
Insufficient or biased moderatorsMedium3 starters → expand by transparent selection. Monthly stats published. Appeals lane
Regulator concernsMediumConsult PMDA / JPMA at launch. Invite observer participation
Low-quality postsLowReputation + Vote naturally sort. Author trustworthiness made visible

13Success metrics & cost

Success metrics by phase

MetricEnd of Phase 1End of Phase 2End of Phase 3
Registered users503001,500
Published prompts30200800
Discussion threads205003,000
Avg comments per prompt2510
Adoption reports10100500
Fork rate (% of published)10%25%40%
Participating orgs (declared)52060

Development & operating cost

ItemPhase 0-1Phase 2Phase 3+
Dev effort (person-months)0.524
VPS / infraexisting (~$0)$20-40 / mo$100-200 / mo
External APIs (LLM)0$50 / mo$500 / mo
Moderation laboreditorialeditorialindustry committee (paid)
Approximate total~ $0$70-90 / mo$600-700 / mo

Initial decision checkpoint

At Phase 0 end (~ 1 month in), assess:

If not, defer Phase 1 and re-design or stop. Phase 0's goal is not "build" but "verify it's worth building".

In closing

Promotional-material work in pharma has been "a closed-loop craft of expertise inside each company". The AI-prompt era opens a chance to turn that closed loop into shared public good across the industry.

The Prompt Commons proposed here is technically modest — a standard web stack, PostgreSQL relationships, Markdown and Git-like versioning. The hard part is not technical. It is the first few companies showing that opening is possible. Once that lift happens, network effects do the rest.

If, after reading this, you find one prompt at your own company that could be opened, that is the first Phase 0 post. Beginnings are always one post at a time.