Technology Transfer API

The Technology Transfer API provides full CRUD operations for managing clinical-to-commercial technology transfers. All procedures require organization-level authentication (orgProcedure) and enforce tenant isolation through organizationId.

There are two routers:

  • techTransfer.* — Base transfer model with steps (legacy)
  • techTransferProjects.* — Extended TT model with phases, tasks, batches, parameters, knowledge sessions, risks, and regulatory checklists (39 procedures)

This reference documents the extended techTransferProjects router.


Project CRUD

techTransferProjects.createProject

Create a new technology transfer project.

POST /api/trpc/techTransferProjects.createProject

Input Schema:

{
  name: string;                     // 1-200 chars, required
  productName: string;              // 1-200 chars, required
  molecule?: string;                // max 200 chars
  dosageForm?: string;              // max 200 chars
  therapeuticArea?: string;         // max 200 chars
  sendingSiteId: string;            // required
  sendingSiteName: string;          // 1-200 chars, required
  sendingSiteCountry?: string;      // max 100 chars
  receivingSiteId: string;          // required
  receivingSiteName: string;        // 1-200 chars, required
  receivingSiteCountry?: string;    // max 100 chars
  description?: string;             // max 2000 chars
  projectManagerId?: string;        // user ID
  sendingSiteLeadId?: string;       // user ID
  receivingSiteLeadId?: string;     // user ID
  targetCompletionDate?: string;    // ISO 8601
  regulatorySubmissionDate?: string; // ISO 8601
}

Response: The created ttProject record.

Example:

{
  "json": {
    "name": "Aspirin 500mg Transfer — Berlin to Dublin",
    "productName": "Aspirin 500mg Tablets",
    "molecule": "Acetylsalicylic acid",
    "dosageForm": "Solid oral",
    "therapeuticArea": "Pain management",
    "sendingSiteId": "site_berlin_01",
    "sendingSiteName": "Berlin Manufacturing",
    "sendingSiteCountry": "Germany",
    "receivingSiteId": "site_dublin_01",
    "receivingSiteName": "Dublin Production",
    "receivingSiteCountry": "Ireland",
    "targetCompletionDate": "2027-06-30T00:00:00.000Z"
  }
}

techTransferProjects.listProjects

Paginated list of TT projects with filters.

POST /api/trpc/techTransferProjects.listProjects

Input Schema:

{
  phase?: "planning" | "characterization" | "scale_up" | "ppq"
    | "cleaning_validation" | "regulatory" | "complete" | "on_hold" | "cancelled";
  status?: "draft" | "active" | "on_hold" | "complete" | "cancelled";
  search?: string;     // free-text search on name or productName
  limit?: number;      // 1-100, default 20
  offset?: number;     // default 0
}

Response:

{
  items: TtProject[];
  total: number;
}

techTransferProjects.getProject

Get a project by ID with child entity counts.

POST /api/trpc/techTransferProjects.getProject

Input: { id: string }

Response: Project record plus:

{
  ...project,
  phaseCount: number;
  taskCount: number;
  batchCount: number;
  parameterCount: number;
  riskCount: number;
  regulatoryItemCount: number;
}

techTransferProjects.updateProject

Partial update of project fields.

POST /api/trpc/techTransferProjects.updateProject

Input: { id: string } plus any project field as optional. Date fields accept ISO 8601 strings or null to clear.

Response: Updated project record.


techTransferProjects.deleteProject

Delete a TT project. All child entities (phases, tasks, batches, parameters, knowledge sessions, risks, regulatory items) are cascade-deleted.

POST /api/trpc/techTransferProjects.deleteProject

Input: { id: string }

Response: Deleted project record.

This is a destructive operation. All child data (phases, tasks, batches, parameters, sessions, risks, regulatory items) will be permanently deleted.


Phase CRUD

techTransferProjects.createPhase

Create a phase within a TT project.

POST /api/trpc/techTransferProjects.createPhase

Input Schema:

{
  projectId: string;               // required
  name: string;                    // 1-200 chars, required
  type: "characterization" | "analytical_transfer" | "scale_up"
    | "engineering_batches" | "ppq" | "cleaning_validation"
    | "packaging_validation" | "stability" | "regulatory"
    | "knowledge_transfer" | "custom";
  sortOrder: number;               // integer >= 0
  startDate?: string;              // ISO 8601
  endDate?: string;                // ISO 8601
}

Response: Created phase record.


techTransferProjects.updatePhase

Update a phase. Automatically recalculates completionPct from child tasks.

POST /api/trpc/techTransferProjects.updatePhase

Input Schema:

{
  id: string;                      // required
  name?: string;
  type?: string;                   // phase type enum
  sortOrder?: number;
  status?: "not_started" | "in_progress" | "complete" | "on_hold" | "skipped";
  startDate?: string | null;
  endDate?: string | null;
}

Response: Updated phase with recalculated completionPct.


techTransferProjects.listPhases

List all phases for a project, ordered by sortOrder.

POST /api/trpc/techTransferProjects.listPhases

Input: { projectId: string }

Response: TtPhase[]


techTransferProjects.reorderPhases

Bulk update phase sort order.

POST /api/trpc/techTransferProjects.reorderPhases

Input:

{
  projectId: string;
  orderedPhaseIds: string[];  // min 1 item
}

Each phase receives its array index as sortOrder.

Response: { success: true }


techTransferProjects.deletePhase

Delete a phase. Child tasks cascade-delete. Batches linked to this phase have their phaseId set to null.

POST /api/trpc/techTransferProjects.deletePhase

Input: { id: string }

Response: Deleted phase record.


Task CRUD

techTransferProjects.createTask

Create a task within a phase.

POST /api/trpc/techTransferProjects.createTask

Input Schema:

{
  projectId: string;               // required
  phaseId: string;                 // required
  title: string;                   // 1-500 chars, required
  description?: string;            // max 2000 chars
  siteResponsibility: "sending" | "receiving" | "both" | "joint";
  assigneeId?: string;             // user ID
  priority?: "critical" | "high" | "medium" | "low";  // default "medium"
  dueDate?: string;                // ISO 8601
  predecessorTaskId?: string;      // task dependency
  requiresSignOff?: boolean;       // default false
  sortOrder?: number;              // default 0
}

Response: Created task record.


techTransferProjects.updateTask

Partial update of a task. Automatically recalculates parent phase completionPct. When status is set to "complete", completedDate is auto-set.

POST /api/trpc/techTransferProjects.updateTask

Input Schema:

{
  id: string;                      // required
  title?: string;
  description?: string | null;
  siteResponsibility?: "sending" | "receiving" | "both" | "joint";
  assigneeId?: string | null;
  status?: "not_started" | "in_progress" | "blocked" | "complete" | "skipped";
  priority?: "critical" | "high" | "medium" | "low";
  dueDate?: string | null;
  predecessorTaskId?: string | null;
  requiresSignOff?: boolean;
  sortOrder?: number;
  signedOffBy?: string | null;
  signedOffAt?: string | null;
}

Response: Updated task record.


techTransferProjects.listTasks

Paginated task list with filters.

POST /api/trpc/techTransferProjects.listTasks

Input Schema:

{
  projectId: string;               // required
  phaseId?: string;                // filter by phase
  status?: "not_started" | "in_progress" | "blocked" | "complete" | "skipped";
  siteResponsibility?: "sending" | "receiving" | "both" | "joint";
  assigneeId?: string;
  limit?: number;                  // 1-200, default 50
  offset?: number;                 // default 0
}

Response: { items: TtTask[], total: number }


techTransferProjects.deleteTask

Delete a task. Clears predecessor references on dependent tasks. Recalculates parent phase completionPct.

POST /api/trpc/techTransferProjects.deleteTask

Input: { id: string }

Response: Deleted task record.


techTransferProjects.bulkUpdateTaskStatus

Batch update task statuses. Recalculates completionPct for all affected phases.

POST /api/trpc/techTransferProjects.bulkUpdateTaskStatus

Input Schema:

{
  taskIds: string[];    // 1-200 task IDs
  newStatus: "not_started" | "in_progress" | "blocked" | "complete" | "skipped";
}

Response: { updatedCount: number }


techTransferProjects.applyProjectTemplate

Populate a project with predefined phases and tasks based on dosage form.

POST /api/trpc/techTransferProjects.applyProjectTemplate

Input Schema:

{
  projectId: string;
  templateType: "solid_oral" | "injectable" | "biologic";
}

Response:

{
  templateType: string;
  phasesCreated: number;
  tasksCreated: number;
}

The project must have zero existing phases. If phases already exist, the procedure throws a CONFLICT error.


Batch CRUD

techTransferProjects.createBatch

Create a batch campaign record.

POST /api/trpc/techTransferProjects.createBatch

Input Schema:

{
  projectId: string;               // required
  phaseId?: string;                // optional link to phase
  batchNumber: string;             // 1-100 chars, unique within project
  batchType: "lab" | "engineering" | "ppq" | "validation" | "stability" | "commercial";
  site: "sending" | "receiving" | "both" | "joint";
  productionDate?: string;         // ISO 8601
  targetYield?: string;            // numeric string (e.g., "95.5")
  batchSize?: string;
  batchSizeUnit?: string;          // max 50 chars
  notes?: string;
}

Response: Created batch record.


techTransferProjects.updateBatch

Partial update of a batch.

POST /api/trpc/techTransferProjects.updateBatch

Input Schema:

{
  id: string;                      // required
  batchNumber?: string;
  batchType?: string;              // batch type enum
  site?: string;                   // site responsibility enum
  productionDate?: string | null;
  status?: "planned" | "in_progress" | "complete_pass" | "complete_fail"
    | "under_review" | "voided";
  yield?: string | null;           // actual yield as decimal string
  targetYield?: string | null;
  deviationCount?: number;         // integer >= 0
  capaCount?: number;              // integer >= 0
  batchSize?: string | null;
  batchSizeUnit?: string | null;
  notes?: string | null;
  phaseId?: string | null;
}

Response: Updated batch record.


techTransferProjects.deleteBatch

Delete a batch record.

POST /api/trpc/techTransferProjects.deleteBatch

Input: { id: string }

Response: Deleted batch record.


techTransferProjects.listBatches

Paginated batch list with filters.

POST /api/trpc/techTransferProjects.listBatches

Input Schema:

{
  projectId: string;               // required
  batchType?: "lab" | "engineering" | "ppq" | "validation" | "stability" | "commercial";
  status?: "planned" | "in_progress" | "complete_pass" | "complete_fail"
    | "under_review" | "voided";
  site?: "sending" | "receiving" | "both" | "joint";
  limit?: number;                  // 1-100, default 20
  offset?: number;                 // default 0
}

Response: { items: TtBatch[], total: number }


techTransferProjects.getBatchSummary

Aggregate batch statistics for a project.

POST /api/trpc/techTransferProjects.getBatchSummary

Input: { projectId: string }

Response:

{
  totalBatches: number;
  byType: Record<string, { total: number; pass: number; fail: number }>;
  avgYield: number | null;         // rounded to 2 decimal places
  totalDeviations: number;
  totalCapas: number;
  trend: Array<{
    month: string;                 // "YYYY-MM"
    total: number;
    pass: number;
    fail: number;
  }>;
}

techTransferProjects.linkDeviationToBatch

Associate a problem sheet (deviation) with a batch. Atomically increments the batch’s deviationCount.

POST /api/trpc/techTransferProjects.linkDeviationToBatch

Input:

{
  batchId: string;
  problemSheetId: string;
}

Response: Updated batch record.


Parameter CRUD

techTransferProjects.createParameter

Create a CPP or CQA parameter. Auto-computes the inSpec flag.

POST /api/trpc/techTransferProjects.createParameter

Input Schema:

{
  projectId: string;               // required
  name: string;                    // 1-200 chars, required
  parameterType: "cpp" | "cqa";    // required
  unit?: string;                   // max 50 chars
  targetValue?: string;            // numeric string
  parLow?: string;                 // PAR lower bound
  parHigh?: string;                // PAR upper bound
  sourceSiteValue?: string;        // observed at sending site
  receivingSiteValue?: string;     // observed at receiving site
  specification?: string;          // max 500 chars
  notes?: string;
}

inSpec Computation: If receivingSiteValue, parLow, and parHigh are all provided, inSpec is true when parLow <= receivingSiteValue <= parHigh.

Response: Created parameter record.


techTransferProjects.updateParameter

Partial update with automatic inSpec recomputation.

POST /api/trpc/techTransferProjects.updateParameter

Input: { id: string } plus any parameter field as optional.

Response: Updated parameter record.


techTransferProjects.deleteParameter

Delete a CPP/CQA parameter.

POST /api/trpc/techTransferProjects.deleteParameter

Input: { id: string }

Response: Deleted parameter record.


techTransferProjects.listParameters

List parameters for a project with optional type filter.

POST /api/trpc/techTransferProjects.listParameters

Input:

{
  projectId: string;               // required
  parameterType?: "cpp" | "cqa";   // filter by type
}

Response: TtParameter[] ordered by name ascending.


techTransferProjects.getParameterComparison

Side-by-side comparison of source vs. receiving site values.

POST /api/trpc/techTransferProjects.getParameterComparison

Input: { projectId: string }

Response:

{
  parameters: Array<{
    id: string;
    name: string;
    parameterType: "cpp" | "cqa";
    unit: string | null;
    targetValue: string | null;
    parLow: string | null;
    parHigh: string | null;
    sourceSiteValue: string | null;
    receivingSiteValue: string | null;
    inSpec: boolean | null;
    deviation: string | null;       // receiving - source
    deviationPct: number | null;    // percentage deviation
    specification: string | null;
  }>;
  summary: {
    total: number;
    inSpec: number;
    outOfSpec: number;
    pending: number;
    compliancePct: number;
  };
}

techTransferProjects.importParametersFromCsv

Bulk import parameters from parsed CSV data (up to 500 rows).

POST /api/trpc/techTransferProjects.importParametersFromCsv

Input Schema:

{
  projectId: string;
  csvData: Array<{
    name: string;                  // 1-200 chars
    parameterType: "cpp" | "cqa";
    unit?: string;                 // max 50 chars
    targetValue?: string;
    parLow?: string;
    parHigh?: string;
  }>;  // 1-500 items
}

Response: { importedCount: number }


Knowledge Session CRUD

techTransferProjects.createKnowledgeSession

Create a knowledge transfer session log.

POST /api/trpc/techTransferProjects.createKnowledgeSession

Input Schema:

{
  projectId: string;               // required
  topic: string;                   // 1-300 chars, required
  description?: string;            // max 2000 chars
  sessionDate: string;             // ISO 8601, required
  durationMinutes?: number;        // integer >= 0
  sendingParticipants?: Array<{
    userId?: string;
    name: string;
    role: string;
  }>;
  receivingParticipants?: Array<{
    userId?: string;
    name: string;
    role: string;
  }>;
  keyFindings?: string;            // max 5000 chars
  actionItems?: string;            // max 5000 chars
}

Response: Created session record.


techTransferProjects.updateKnowledgeSession

Partial update of a knowledge session.

POST /api/trpc/techTransferProjects.updateKnowledgeSession

Input: { id: string } plus any session field as optional.

Response: Updated session record.


techTransferProjects.deleteKnowledgeSession

Delete a knowledge session. Blocked if both sites have signed off (returns FORBIDDEN).

POST /api/trpc/techTransferProjects.deleteKnowledgeSession

Input: { id: string }

Response: Deleted session record.

Sessions with dual sign-off (both sending and receiving confirmed) cannot be deleted. This is enforced atomically in the database query to prevent race conditions.


techTransferProjects.listKnowledgeSessions

Paginated session list, ordered by session date descending.

POST /api/trpc/techTransferProjects.listKnowledgeSessions

Input:

{
  projectId: string;               // required
  limit?: number;                  // 1-100, default 20
  offset?: number;                 // default 0
}

Response: { items: TtKnowledgeSession[], total: number }


techTransferProjects.signOffSession

Apply a dual site sign-off to a knowledge session.

POST /api/trpc/techTransferProjects.signOffSession

Input:

{
  sessionId: string;
  site: "sending" | "receiving";
}

The procedure sets:

  • signedOffSending / signedOffReceiving to true
  • signedOffSendingBy / signedOffReceivingBy to the current user
  • signedOffSendingAt / signedOffReceivingAt to the current timestamp

Throws CONFLICT if the specified site has already signed off.

Response: Updated session record.


Risk CRUD

techTransferProjects.createRisk

Create a risk with auto-computed riskScore = probability x impact.

POST /api/trpc/techTransferProjects.createRisk

Input Schema:

{
  projectId: string;               // required
  title: string;                   // 1-300 chars, required
  description?: string;            // max 2000 chars
  category: "scale_up" | "equipment_compatibility" | "analytical_method"
    | "supply_chain" | "regulatory" | "personnel" | "timeline"
    | "process_robustness" | "cleaning" | "stability" | "ip_knowledge" | "custom";
  probability: number;             // 1-5, required
  impact: number;                  // 1-5, required
  mitigation?: string;             // max 2000 chars
  contingency?: string;            // max 2000 chars
  ownerId?: string;                // user ID
  dueDate?: string;                // ISO 8601
  linkedBatchId?: string;          // link to a batch
  linkedTaskId?: string;           // link to a task
}

Response: Created risk record with computed riskScore.


techTransferProjects.updateRisk

Partial update. Recomputes riskScore when probability or impact changes. Auto-sets closedDate when status transitions to "closed".

POST /api/trpc/techTransferProjects.updateRisk

Input: { id: string } plus any risk field as optional.

Response: Updated risk record.


techTransferProjects.deleteRisk

Delete a risk record.

POST /api/trpc/techTransferProjects.deleteRisk

Input: { id: string }

Response: Deleted risk record.


techTransferProjects.listRisks

Paginated risk list sorted by riskScore descending (highest risk first).

POST /api/trpc/techTransferProjects.listRisks

Input Schema:

{
  projectId: string;               // required
  category?: string;               // risk category enum
  status?: "identified" | "mitigating" | "monitoring" | "closed" | "materialized";
  minRiskScore?: number;           // 1-25, filter risks >= this score
  limit?: number;                  // 1-100, default 50
  offset?: number;                 // default 0
}

Response: { items: TtRisk[], total: number }


techTransferProjects.getRiskMatrix

5x5 probability-impact matrix with per-cell risk lists.

POST /api/trpc/techTransferProjects.getRiskMatrix

Input: { projectId: string }

Response:

{
  matrix: Array<{
    probability: number;           // 1-5
    impact: number;                // 1-5
    count: number;
    risks: Array<{
      id: string;
      title: string;
      category: string;
      status: string;
      riskScore: number;
    }>;
  }>;  // 25 cells (5x5)
  summary: {
    total: number;
    critical: number;              // riskScore >= 15
    high: number;                  // riskScore 9-14
    medium: number;                // riskScore 4-8
    low: number;                   // riskScore 1-3
    avgScore: number;
  };
}

Regulatory Checklist

techTransferProjects.createRegulatoryItem

Create a regulatory checklist item.

POST /api/trpc/techTransferProjects.createRegulatoryItem

Input Schema:

{
  projectId: string;               // required
  section: "asmf" | "dmf" | "ctd_module_3" | "cep" | "gmp_certificate"
    | "stability_data" | "batch_analysis" | "comparability_protocol"
    | "site_master_file" | "cleaning_validation_report"
    | "process_validation_report" | "analytical_method_validation" | "custom";
  itemName: string;                // 1-300 chars, required
  description?: string;            // max 2000 chars
  responsibleSite: "sending" | "receiving" | "both" | "joint";
  assigneeId?: string;             // user ID
  dueDate?: string;                // ISO 8601
  documentRef?: string;            // max 500 chars
  sortOrder: number;               // integer >= 0, required
}

Response: Created regulatory item record.


techTransferProjects.updateRegulatoryItem

Partial update. Auto-sets completedDate when status transitions to "approved".

POST /api/trpc/techTransferProjects.updateRegulatoryItem

Input: { id: string } plus any regulatory item field as optional.

Response: Updated regulatory item record.


techTransferProjects.deleteRegulatoryItem

Delete a regulatory checklist item.

POST /api/trpc/techTransferProjects.deleteRegulatoryItem

Input: { id: string }

Response: Deleted regulatory item record.


techTransferProjects.listRegulatoryItems

List all regulatory items grouped by section.

POST /api/trpc/techTransferProjects.listRegulatoryItems

Input: { projectId: string }

Response:

{
  items: TtRegulatoryChecklistItem[];     // flat list sorted by section + sortOrder
  grouped: Record<string, TtRegulatoryChecklistItem[]>;  // grouped by section
}

techTransferProjects.getRegulatoryReadiness

Per-section completion percentages.

POST /api/trpc/techTransferProjects.getRegulatoryReadiness

Input: { projectId: string }

Response:

{
  sections: Array<{
    section: string;
    total: number;
    complete: number;
    completionPct: number;         // 0-100
  }>;
  overall: {
    total: number;
    complete: number;
    completionPct: number;         // 0-100
  };
}

Items with status "approved" or "not_applicable" count as complete.


techTransferProjects.applyRegulatoryTemplate

Populate the checklist with a predefined regulatory template.

POST /api/trpc/techTransferProjects.applyRegulatoryTemplate

Input:

{
  projectId: string;
  template: "eu" | "us" | "ich";
}

Response: { template: string, itemsCreated: number }

TemplateFocusItems
euEMA — ASMF, CEP, EEA GMP~22
usFDA — DMF, FEI, SUPAC~20
ichHarmonized Q-series (Q2-Q12)~21

The project must have zero existing regulatory items. If items already exist, the procedure throws a CONFLICT error.


Dashboard and Readiness

techTransferProjects.getDashboard

Comprehensive project dashboard with aggregated data from all child entities.

POST /api/trpc/techTransferProjects.getDashboard

Input: { projectId: string }

Response:

{
  project: TtProject;
  readinessScore: number;                 // 0-100
  phaseProgress: Array<{
    id: string;
    name: string;
    type: string;
    status: string;
    completionPct: number;
    taskCount: number;
    completedTaskCount: number;
  }>;
  taskSummary: {
    total: number;
    completed: number;
    blocked: number;
    overdue: number;
    completionPct: number;
  };
  batchSummary: {
    total: number;
    passed: number;
    failed: number;
    avgYield: number | null;
    totalDeviations: number;
    passRate: number | null;
  };
  parameterSummary: {
    total: number;
    inSpec: number;
    outOfSpec: number;
    pending: number;
  };
  regulatoryReadiness: {
    total: number;
    complete: number;
    completionPct: number;
  };
  knowledgeTransfer: {
    totalSessions: number;
    fullySignedOff: number;
    coveragePct: number;
  };
  topRisks: Array<{                       // top 5 by riskScore
    id: string;
    title: string;
    category: string;
    probability: number;
    impact: number;
    riskScore: number;
    status: string;
  }>;
  upcomingMilestones: Array<{             // tasks due in next 30 days
    id: string;
    title: string;
    phaseId: string;
    dueDate: Date;
    priority: string;
    siteResponsibility: string;
    status: string;
  }>;
}

techTransferProjects.calculateReadinessScore

Compute and persist the composite weighted readiness score.

POST /api/trpc/techTransferProjects.calculateReadinessScore

Input: { projectId: string }

Response:

{
  readinessScore: number;                 // 0-100
  breakdown: {
    taskCompletion: {
      weight: 0.25;
      rawPct: number;
      weighted: number;
      totalTasks: number;
      completedTasks: number;
    };
    batchPassRate: {
      weight: 0.25;
      rawPct: number;
      weighted: number;
      totalCompleted: number;
      passed: number;
    };
    deviationCount: {
      weight: 0.15;
      rawPct: number;                     // 100 - (deviations * 10), min 0
      weighted: number;
      totalDeviations: number;
    };
    openCapaCount: {
      weight: 0.10;
      rawPct: number;                     // 100 - (capas * 10), min 0
      weighted: number;
      totalCapas: number;
    };
    regulatoryCompleteness: {
      weight: 0.15;
      rawPct: number;
      weighted: number;
      total: number;
      complete: number;
    };
    knowledgeTransferCoverage: {
      weight: 0.10;
      rawPct: number;
      weighted: number;
      totalSessions: number;
      fullySignedOff: number;
    };
  };
}

Call this procedure after significant milestones (batch campaign completion, regulatory item approval, knowledge session sign-off) to keep the readiness score current.


Error Codes

All procedures may return these standard tRPC errors:

CodeWhen
NOT_FOUNDProject, phase, task, batch, parameter, session, risk, or regulatory item not found (or does not belong to current organization)
CONFLICTTemplate applied to project that already has phases/regulatory items; session sign-off already completed
BAD_REQUESTUnknown template type
FORBIDDENAttempt to delete a knowledge session with dual sign-off
UNAUTHORIZEDMissing or invalid authentication