Skip to content

Latest commit

 

History

History
1661 lines (1354 loc) · 22.9 KB

File metadata and controls

1661 lines (1354 loc) · 22.9 KB

Protocol Guide API Types Reference

Complete TypeScript type definitions for all tRPC procedures.

Table of Contents


Core Types

User

interface User {
  id: number;
  supabaseId: string;
  email: string | null;
  name: string | null;
  role: "user" | "admin";
  tier: "free" | "pro" | "enterprise";
  selectedCountyId: number | null;
  stripeCustomerId: string | null;
  subscriptionStatus: string | null;
  subscriptionEndDate: Date | null;
  createdAt: Date;
  updatedAt: Date;
  lastLoginAt: Date | null;
  disclaimerAcknowledgedAt: Date | null;
}

TrpcContext

interface TrpcContext {
  req: Request;
  res: Response;
  user: User | null;
}

System Types

System.Health

Input:

{
  timestamp: number; // >= 0
}

Output:

{
  ok: boolean;
}

System.NotifyOwner

Input:

{
  title: string;    // min 1 char
  content: string;  // min 1 char
}

Output:

{
  success: boolean;
}

Auth Types

Auth.Me

Input: None

Output:

User | null

Auth.Logout

Input: None

Output:

{
  success: true;
}

County Types

County

interface County {
  id: number;
  name: string;
  state: string;
  stateCode: string;
  createdAt: Date;
  updatedAt: Date;
}

Counties.List

Input: None

Output:

{
  counties: County[];
  grouped: Record<string, County[]>; // Grouped by state
}

Counties.Get

Input:

{
  id: number;
}

Output:

County | null

User Types

User.Usage

Input: None

Output:

{
  count: number;   // Queries used today
  limit: number;   // Daily query limit
  tier: "free" | "pro" | "enterprise";
}

User.AcknowledgeDisclaimer

Input: None

Output:

{
  acknowledgedAt: Date;
}

User.HasAcknowledgedDisclaimer

Input: None

Output:

{
  hasAcknowledged: boolean;
}

User.SelectCounty

Input:

{
  countyId: number;
}

Output:

{
  success: boolean;
}

User.Queries

Input:

{
  limit?: number; // 1-100, default 10
}

Output:

Array<{
  id: number;
  queryText: string;
  responseText: string;
  protocolRefs: string[];
  createdAt: Date;
  countyId: number;
}>

User.SavedCounties

Input: None

Output:

{
  counties: County[];
  canAdd: boolean;
  currentCount: number;
  maxAllowed: number;
  tier: "free" | "pro" | "enterprise";
}

User.AddCounty

Input:

{
  countyId: number;
  isPrimary?: boolean; // default false
}

Output:

{
  success: boolean;
  error?: string;
}

User.RemoveCounty

Input:

{
  countyId: number;
}

Output:

{
  success: boolean;
}

User.SetPrimaryCounty

Input:

{
  countyId: number;
}

Output:

{
  success: boolean;
}

User.PrimaryCounty

Input: None

Output:

County | null

Search Types

SearchResultItem

interface SearchResultItem {
  id: number;
  protocolNumber: string;
  protocolTitle: string;
  section: string | null;
  content: string;              // Truncated to 500 chars
  fullContent: string;          // Complete content
  relevanceScore: number;       // 0-1 similarity
  countyId: number;
  sourcePdfUrl: null;
  protocolEffectiveDate: null;
  lastVerifiedAt: null;
  protocolYear: null;
}

Search.Semantic

Input:

{
  query: string;          // 1-500 chars
  countyId?: number;      // Optional county filter
  limit?: number;         // 1-50, default 10
  stateFilter?: string;   // Two-letter state code
}

Output:

{
  results: SearchResultItem[];
  totalFound: number;
  query: string;                  // Original query
  normalizedQuery: string;        // Normalized query
  fromCache: boolean;
  latencyMs: number;
}

Search.GetProtocol

Input:

{
  id: number;
}

Output:

{
  id: number;
  protocolNumber: string;
  protocolTitle: string;
  section: string | null;
  content: string;
  embedding: number[] | null;
  agencyId: number;
  createdAt: Date;
  updatedAt: Date;
} | null

Search.Stats

Input: None

Output:

{
  totalProtocols: number;
  totalAgencies: number;
  lastUpdated: Date;
}

Search.CoverageByState

Input: None

Output:

Array<{
  state: string;
  countyCount: number;
  protocolCount: number;
}>

Search.TotalStats

Input: None

Output:

{
  totalProtocols: number;
  totalAgencies: number;
  totalStates: number;
}

Search.AgenciesByState

Input:

{
  state: string; // Two-letter code
}

Output:

Array<{
  id: number;
  name: string;
  state: string;
  protocolCount: number;
}>

Search.AgenciesWithProtocols

Input:

{
  state?: string; // Optional state filter
}

Output: Same as AgenciesByState

Search.SearchByAgency

Input:

{
  query: string;     // 1-500 chars
  agencyId: number;  // MySQL county ID
  limit?: number;    // 1-50, default 10
}

Output: Same as Search.Semantic


Query Types

Query.Submit

Input:

{
  countyId: number;
  queryText: string; // 1-1000 chars
}

Output:

{
  success: boolean;
  error: string | null;
  response: {
    text: string;                    // Claude's response
    protocolRefs: string[];          // Referenced protocols
    model: string;                   // Claude model used
    tokens: {
      input: number;
      output: number;
    };
    responseTimeMs: number;
    normalizedQuery: string;         // Normalized query
    queryIntent: string;             // Detected intent
    isComplexQuery: boolean;
  } | null;
}

Query.History

Input:

{
  limit?: number; // 1-100, default 50
}

Output:

Array<{
  id: number;
  userId: number;
  countyId: number;
  queryText: string;
  responseText: string;
  protocolRefs: string[];
  createdAt: Date;
}>

SearchHistoryEntry

interface SearchHistoryEntry {
  id: number;
  userId: number;
  queryText: string;
  countyId: number | null;
  timestamp: Date;
  deviceId: string | null;
  synced: boolean;
}

Query.SearchHistory

Input:

{
  limit?: number; // 1-100, default 50
}

Output:

SearchHistoryEntry[]

Query.SyncHistory

Input:

{
  localQueries: Array<{
    queryText: string;          // 1-500 chars
    countyId?: number;
    timestamp: string | Date;
    deviceId?: string;          // Max 64 chars
  }>;
}

Output:

{
  success: boolean;
  merged: number;
  serverHistory: SearchHistoryEntry[];
}

Query.ClearHistory

Input: None

Output:

{
  success: boolean;
}

Query.DeleteHistoryEntry

Input:

{
  entryId: number;
}

Output:

{
  success: boolean;
}

Voice Types

Voice.Transcribe

Input:

{
  audioUrl: string;     // Must be from authorized domain
  language?: string;    // Optional language hint
}

Output:

{
  success: boolean;
  error: string | null;
  text: string | null;
}

Voice.UploadAudio

Input:

{
  audioBase64: string;  // Max 10MB
  mimeType: string;     // e.g., "audio/webm"
}

Output:

{
  url: string; // Storage URL
}

Feedback Types

Feedback.Submit

Input:

{
  category: "error" | "suggestion" | "general";
  subject: string;        // 1-255 chars
  message: string;        // min 1 char
  protocolRef?: string;   // max 255 chars
}

Output:

{
  success: boolean;
  error: string | null;
}

Feedback.MyFeedback

Input: None

Output:

Array<{
  id: number;
  category: "error" | "suggestion" | "general";
  subject: string;
  message: string;
  status: "pending" | "reviewed" | "resolved" | "dismissed";
  createdAt: Date;
  adminNotes?: string;
}>

Contact Types

Contact.Submit

Input:

{
  name: string;      // 1-255 chars
  email: string;     // Valid email, max 320 chars
  message: string;   // 10-5000 chars
}

Output:

{
  success: boolean;
  error: string | null;
}

Subscription Types

Subscription.CreateCheckout

Input:

{
  plan: "monthly" | "annual";
  successUrl: string;  // Valid URL
  cancelUrl: string;   // Valid URL
}

Output:

{
  success: boolean;
  error: string | null;
  url: string | null;  // Stripe checkout URL
}

Subscription.CreatePortal

Input:

{
  returnUrl: string; // Valid URL
}

Output:

{
  success: boolean;
  error: string | null;
  url: string | null; // Customer portal URL
}

Subscription.Status

Input: None

Output:

{
  tier: "free" | "pro" | "enterprise";
  subscriptionStatus: string | null;
  subscriptionEndDate: Date | null;
}

Subscription.CreateDepartmentCheckout

Input:

{
  agencyId: number;
  tier: "starter" | "professional" | "enterprise";
  seatCount: number;      // 1-1000
  interval: "monthly" | "annual";
  successUrl: string;
  cancelUrl: string;
}

Output:

{
  success: boolean;
  error: string | null;
  url: string | null;
}

Admin Types

Admin.ListFeedback

Input:

{
  status?: "pending" | "reviewed" | "resolved" | "dismissed";
  limit?: number;    // 1-100, default 50
  offset?: number;   // default 0
}

Output:

{
  feedback: Array<Feedback>;
  total: number;
}

Admin.UpdateFeedback

Input:

{
  feedbackId: number;
  status: "pending" | "reviewed" | "resolved" | "dismissed";
  adminNotes?: string;
}

Output:

{
  success: boolean;
}

Admin.ListUsers

Input:

{
  tier?: "free" | "pro" | "enterprise";
  role?: "user" | "admin";
  limit?: number;    // 1-100, default 50
  offset?: number;   // default 0
}

Output:

{
  users: User[];
  total: number;
}

Admin.UpdateUserRole

Input:

{
  userId: number;
  role: "user" | "admin";
}

Output:

{
  success: boolean;
}

Admin.ListContactSubmissions

Input:

{
  status?: "pending" | "reviewed" | "resolved";
  limit?: number;    // 1-100, default 50
  offset?: number;   // default 0
}

Output:

{
  submissions: ContactSubmission[];
  total: number;
}

Admin.UpdateContactStatus

Input:

{
  submissionId: number;
  status: "pending" | "reviewed" | "resolved";
}

Output:

{
  success: boolean;
}

AuditLog

interface AuditLog {
  id: number;
  userId: number;
  action: string;
  targetType: string;
  targetId: string;
  details: object;
  createdAt: Date;
}

Admin.GetAuditLogs

Input:

{
  limit?: number;    // 1-100, default 50
  offset?: number;   // default 0
}

Output:

{
  logs: AuditLog[];
  total: number;
}

Agency Admin Types

Agency

interface Agency {
  id: number;
  name: string;
  state: string;
  stateCode: string;
  contactEmail: string | null;
  contactPhone: string | null;
  address: string | null;
  settings: object | null;
  tier: "starter" | "professional" | "enterprise" | null;
  seatCount: number | null;
  createdAt: Date;
  updatedAt: Date;
}

AgencyMember

interface AgencyMember {
  id: number;
  agencyId: number;
  userId: number;
  role: "owner" | "admin" | "protocol_author" | "member";
  joinedAt: Date;
  user?: {
    id: number;
    name: string;
    email: string;
  } | null;
}

AgencyAdmin.MyAgencies

Input: None

Output:

Array<{
  id: number;
  name: string;
  role: "owner" | "admin" | "protocol_author" | "member";
  // ...Agency fields
}>

AgencyAdmin.GetAgency

Input:

{
  agencyId: number;
}

Output:

Agency

AgencyAdmin.UpdateAgency

Input:

{
  agencyId: number;
  name?: string;              // 1-255 chars
  contactEmail?: string;      // Valid email, max 320
  contactPhone?: string;      // Max 20 chars
  address?: string;           // Max 500 chars
  settings?: {
    brandColor?: string;
    allowSelfRegistration?: boolean;
    requireEmailVerification?: boolean;
    protocolApprovalRequired?: boolean;
  };
}

Output:

{
  success: boolean;
}

AgencyAdmin.ListMembers

Input:

{
  agencyId: number;
}

Output:

AgencyMember[]

AgencyAdmin.InviteMember

Input:

{
  agencyId: number;
  email: string;
  role?: "admin" | "protocol_author" | "member"; // default "member"
}

Output:

{
  success: boolean;
  token: string;
}

AgencyAdmin.UpdateMemberRole

Input:

{
  agencyId: number;
  memberId: number;
  role: "admin" | "protocol_author" | "member";
}

Output:

{
  success: boolean;
}

AgencyAdmin.RemoveMember

Input:

{
  agencyId: number;
  memberId: number;
}

Output:

{
  success: boolean;
}

ProtocolVersion

interface ProtocolVersion {
  id: number;
  agencyId: number;
  protocolNumber: string;
  title: string;
  version: string;
  status: "draft" | "review" | "approved" | "published" | "archived";
  sourceFileUrl: string;
  effectiveDate: Date | null;
  metadata: object | null;
  createdBy: number;
  approvedBy: number | null;
  createdAt: Date;
  updatedAt: Date;
}

AgencyAdmin.ListProtocols

Input:

{
  agencyId: number;
  status?: "draft" | "review" | "approved" | "published" | "archived";
  limit?: number;    // 1-100, default 50
  offset?: number;   // default 0
}

Output:

{
  protocols: ProtocolVersion[];
  total: number;
}

AgencyAdmin.UploadProtocol

Input:

{
  agencyId: number;
  fileName: string;           // Max 255 chars
  fileBase64: string;         // Max 20MB
  mimeType?: string;          // default "application/pdf"
  protocolNumber: string;     // Max 50 chars
  title: string;              // Max 255 chars
  version?: string;           // Max 20 chars, default "1.0"
  effectiveDate?: string;     // ISO date string
}

Output:

{
  success: boolean;
  uploadId: number;
  versionId: number;
  fileUrl: string;
}

ProtocolUpload

interface ProtocolUpload {
  id: number;
  agencyId: number;
  userId: number;
  fileName: string;
  fileUrl: string;
  fileSize: number;
  mimeType: string;
  status: "pending" | "processing" | "completed" | "failed";
  progress: number | null;
  error: string | null;
  createdAt: Date;
  updatedAt: Date;
}

AgencyAdmin.GetUploadStatus

Input:

{
  agencyId: number;
  uploadId: number;
}

Output:

ProtocolUpload

AgencyAdmin.UpdateProtocolStatus

Input:

{
  agencyId: number;
  versionId: number;
  status: "draft" | "review" | "approved" | "published" | "archived";
}

Output:

{
  success: boolean;
}

AgencyAdmin.PublishProtocol

Input:

{
  agencyId: number;
  versionId: number;
}

Output:

{
  success: boolean;
}

AgencyAdmin.ArchiveProtocol

Input:

{
  agencyId: number;
  versionId: number;
}

Output:

{
  success: boolean;
}

AgencyAdmin.ListVersions

Input:

{
  agencyId: number;
  protocolNumber: string;
}

Output:

ProtocolVersion[]

AgencyAdmin.CreateVersion

Input:

{
  agencyId: number;
  fromVersionId: number;
  newVersion: string;    // Max 20 chars
  changes?: string;      // Change log
}

Output:

{
  success: boolean;
  versionId: number;
}

Integration Types

IntegrationPartner

type IntegrationPartner = "imagetrend" | "esos" | "zoll" | "emscloud";

Integration.LogAccess

Input:

{
  partner: IntegrationPartner;
  agencyId?: string;          // Max 100 chars
  agencyName?: string;        // Max 255 chars
  searchTerm?: string;        // Max 500 chars
  userAge?: number;           // NOT STORED (HIPAA)
  impression?: string;        // NOT STORED (HIPAA)
  responseTimeMs?: number;
  resultCount?: number;
}

Output:

{
  success: boolean;
  logged: boolean;
  requestId: string;
}

Integration.GetStats

Input:

{
  partner?: IntegrationPartner;
  days?: number;  // 1-365, default 30
}

Output:

{
  stats: Array<{
    partner: string;
    accessCount: number;
    uniqueAgencies: number;
    avgResponseTimeMs: number | null;
  }>;
  total: number;
  periodDays: number;
}

Integration.GetRecentLogs

Input:

{
  partner?: IntegrationPartner;
  limit?: number;   // 1-100, default 50
  offset?: number;  // default 0
}

Output:

{
  logs: IntegrationLog[];
  total: number;
}

Integration.GetDailyUsage

Input:

{
  partner?: IntegrationPartner;
  days?: number;  // 1-90, default 30
}

Output:

{
  dailyUsage: Array<{
    date: string;
    partner: string;
    count: number;
  }>;
}

Referral Types

ReferralTier

type ReferralTier = "bronze" | "silver" | "gold" | "platinum" | "ambassador";

Referral.GetMyReferralCode

Input: None

Output:

{
  code: string;
  usesCount: number;
  createdAt: Date;
}

Referral.GetMyStats

Input: None

Output:

{
  totalReferrals: number;
  successfulReferrals: number;
  pendingReferrals: number;
  proDaysEarned: number;
  creditsEarned: number;
  currentTier: ReferralTier;
  rank: number | null;
  nextTierProgress: number;        // 0-100
  nextTierName: string | null;
  referralsToNextTier: number;
}

Referral.GetMyReferrals

Input:

{
  limit?: number;   // 1-50, default 20
  offset?: number;  // default 0
}

Output:

{
  referrals: Array<{
    id: number;
    redeemedAt: Date;
    convertedToPaid: boolean;
    conversionDate: Date | null;
    reward: object;
    referredUser: {
      name: string;
      email: string; // Masked
    };
  }>;
  total: number;
}

Referral.ValidateCode

Input:

{
  code: string; // 1-20 chars
}

Output:

{
  valid: boolean;
  error?: string;
  referrerName?: string;
  benefits?: {
    trialDays: number;
    description: string;
  };
}

Referral.RedeemCode

Input:

{
  code: string; // 1-20 chars
}

Output:

{
  success: boolean;
  benefit: string;
}

Referral.GetShareTemplates

Input: None

Output:

{
  sms: string;
  whatsapp: string;
  email: {
    subject: string;
    body: string;
  };
  generic: string;
  shareUrl: string;
  code: string;
}

Referral.GetLeaderboard

Input:

{
  limit?: number;   // 1-100, default 25
  timeframe?: "all_time" | "this_month" | "this_week"; // default "all_time"
}

Output:

{
  leaderboard: Array<{
    rank: number;
    userId: number;
    userName: string;
    totalReferrals: number;
    successfulReferrals: number;
    tier: string;
  }>;
  timeframe: string;
}

ViralEventType

type ViralEventType =
  | "referral_code_generated"
  | "referral_code_shared"
  | "referral_code_copied"
  | "share_button_tapped"
  | "shift_share_shown"
  | "shift_share_accepted"
  | "shift_share_dismissed"
  | "social_share_completed";

Referral.TrackViralEvent

Input:

{
  eventType: ViralEventType;
  metadata?: {
    shareMethod?: "sms" | "whatsapp" | "email" | "copy" | "qr";
    referralCode?: string;
    platform?: string;
  };
}

Output:

{
  tracked: boolean;
}

Type Inference Utilities

Infer Input Types

import type { AppRouter } from "../server/routers";
import { inferRouterInputs } from "@trpc/server";

type RouterInput = inferRouterInputs<AppRouter>;

// Example: Get input type for any procedure
type QuerySubmitInput = RouterInput["query"]["submit"];
type SearchSemanticInput = RouterInput["search"]["semantic"];

Infer Output Types

import type { AppRouter } from "../server/routers";
import { inferRouterOutputs } from "@trpc/server";

type RouterOutput = inferRouterOutputs<AppRouter>;

// Example: Get output type for any procedure
type QuerySubmitOutput = RouterOutput["query"]["submit"];
type SearchSemanticOutput = RouterOutput["search"]["semantic"];

Infer Procedure Types

import type { AppRouter } from "../server/routers";
import { inferProcedureOutput, inferProcedureInput } from "@trpc/server";

// For specific procedures
type UserUsageOutput = inferProcedureOutput<AppRouter["user"]["usage"]>;
type UserUsageInput = inferProcedureInput<AppRouter["user"]["usage"]>;

Validation Schemas

All input validation is handled by Zod schemas. Here are common validation patterns:

String Validation

z.string()
  .min(1)                    // Required
  .max(255)                  // Max length
  .email()                   // Email format
  .url()                     // URL format
  .optional()                // Optional field
  .default("value")          // Default value

Number Validation

z.number()
  .int()                     // Integer only
  .min(0)                    // Minimum value
  .max(100)                  // Maximum value
  .default(10)               // Default value

Enum Validation

z.enum(["option1", "option2", "option3"])

Object Validation

z.object({
  field1: z.string(),
  field2: z.number().optional(),
})

Array Validation

z.array(z.object({
  item: z.string()
}))

Support

For type-related questions or issues, contact: support@protocolguide.app