Skip to content

Brisket API Surface

Brisket is the Next.js 16 user-facing frontend. Its public surface is small — most data flow happens through tRPC procedures over POST /api/trpc/[trpc] rather than bespoke REST endpoints. Upstream traffic terminates at sirloin (gRPC via ConnectRPC) plus a handful of third parties (Strapi, Intercom, Clerk).

Source: apps/brisket/src/app/api/.

Path Method Auth Purpose Source
/api/health GET Public Liveness probe — returns 200 { status: "ok" }. app/api/health/route.ts
/api/trpc/[trpc] GET, POST Clerk session via tRPC ctx Single tRPC entrypoint. Uses @trpc/server/adapters/fetch fetchRequestHandler. app/api/trpc/[trpc]/route.ts
/api/webhook/clerk POST Svix HMAC (CLERK_WEBHOOK_SECRET) Receives Clerk lifecycle webhooks (user.created, session.created) and emits PostHog events. app/api/webhook/clerk/route.ts

/api/health and /api/webhook/clerk are listed as public routes in apps/brisket/src/middleware.ts (isPublicRoute). All other routes — including the tRPC entrypoint — are gated by Clerk via auth.protect() in the same middleware.

Grep for "use server" returns no matches under apps/brisket/src (verified). All mutations go through tRPC. Use grep -rn '"use server"' apps/brisket/src to refresh; add server actions here if introduced.

Root composition: apps/brisket/src/server/api/root.ts. All procedures use protectedProcedure (Clerk-authenticated) unless explicitly marked otherwise. Inputs are validated with Zod; outputs from sirloin are returned as Connect/protobuf message shapes (pkg/sirloin/v5/*_pb). Error formatter (server/api/trpc.ts) attaches zodError to data for client-side field hints.

character — apps/brisket/src/server/api/routers/character.ts

Section titled “character — apps/brisket/src/server/api/routers/character.ts”

Owns character CRUD, dataset uploads, and welcome-media triggers. Calls ctx.api (sirloin SirloinService).

Procedure Type Input (Zod) Output Upstream
getAllCharacters query – Character[] sirloin.listCharacters
getCharacter query { character_id } Character | null sirloin.listCharacters (filtered)
fetchCharacter / fetchCharacters mutation { character_id }? Character[] sirloin.listCharacters
createCharacter mutation name, handles, Gender, CharacterModelType, pack id, is_nsfw Character sirloin.createCharacter
getUploadRefImageUrl, getUploadImageUrls, getKitsuneUploadUrls mutation image meta signed upload URLs sirloin.getUpload*
triggerRefImageUploaded mutation upload id – sirloin
updateCharacter, deleteCharacter, updateCharacterReferenceDataset mutation character partials Character sirloin.updateCharacter / deleteCharacter
verifyDatasetImage, removeDatasetImage mutation image id – sirloin
generateKitsunePreviewImages, submitKitsuneCharacterForTraining mutation id – sirloin
submitWelcomeMediaReview, triggerWelcomeMediaRegeneration, triggerWelcomeExDevOnly mutation – – sirloin
scrapeCharacterInstagramHandle mutation handle metadata sirloin

kyc — apps/brisket/src/server/api/routers/kyc.ts

Section titled “kyc — apps/brisket/src/server/api/routers/kyc.ts”

Owns account-level KYC start/reset actions. Calls ctx.api (sirloin SirloinService).

Procedure Type Input (Zod) Output Upstream
startKYCVerification mutation { variant: "ri" | "vi" } KYC payload sirloin.startKYCVerification
resetKYCVerification mutation – empty response sirloin.resetKYCVerification

Image generation and library operations.

Procedure Type Notes
createImage mutation enqueue generation in sirloin
getAllMedia query paginated library fetch
toggleMediaFavoriteById, toggleMediaArchivedById mutation per-media flags
fetchGlobalPending query pending generations across user
reportMediaById mutation NSFW / abuse report
markMediaDownload mutation analytics + watermark
getLastMediaGenerationDate mutation rate-limit / banner data
getFailedMediaByIds mutation retry/diagnostic

Billing surface. Uses ctx.billingApi (sirloin BillingService) and validates Cloudflare Turnstile tokens server-side. See brisket-billing-flow.md for the end-to-end sequence.

Procedures: getCurrentUsage, claimOffer, setReferralCode, createPrimerCheckout, submitPaidInvoice, setTokenBillingAddress, createVaultingSession, listProducts, getCountryCode, updateNextBillingDate, getPrimerPaymentMethods, deletePrimerPaymentMethod, setPrimaryPrimerPaymentMethod, validateCouponCode, changePrimerSubscriptionBackToActive, getPrimerSubscriptions, getPrimerTransactions, getPrimerPaymentData, cancelPrimerSubscription, retryDunningPayment, cancelPrimerSubscriptionDowngrade.

getUserProfile (query), updateUserProfile (mutation, { nsfwEnabled? }). Both call ctx.api.getUserProfile / updateUserProfile on sirloin.

Starter packs and example browsing.

getFilteredExamples, getExampleById, getStarterPackList, getMediaTags — all call sirloin pack endpoints.

Virtual influencer shop.

listVIs, getVIdata, reserveCharacter, purchaseShopCharacterWithReservationToken — call sirloin shop endpoints.

createSupportConversation (mutation): looks up the contact in Intercom by primary email and posts a templated support message. Soft-fails (returns undefined) if INTERCOM_API_TOKEN is unset or contact missing.

getCheckoutFaqSection (query): fetches ${STRAPI_URL}/api/checkout-faq-section?populate=* with Authorization: Bearer ${STRAPI_TOKEN}. Cached via Next.js next: { revalidate: 3600 }.

flowchart LR
  Client[Browser] -->|POST /api/trpc/*| Brisket
  Brisket -->|gRPC ConnectRPC| Sirloin[(sirloin)]
  Brisket -->|HTTPS| Strapi[(chuck/Strapi)]
  Brisket -->|HTTPS| Intercom
  Clerk -->|svix webhook| Brisket
  • sirloin — primary upstream. Two clients constructed in server/api/sirloin-api.ts: SirloinService (general — characters, media, profile, packs, shop) and BillingService (subscription router). Transport: createGrpcTransport({ baseUrl: env.SIRLOIN_URL, defaultTimeoutMs: 120_000, peerMaxConcurrentStreams: 500 }). Transport refreshed every 30 minutes via setInterval to mitigate stale H2 connections. Headers built by server/api/grpc-headers.ts.
  • brain — no direct calls from brisket (verified — no brain imports or HTTP/gRPC clients under apps/brisket/src). Brain is reached transitively via sirloin.
  • Strapi (chuck) — REST GET /api/checkout-faq-section with bearer token; ISR-cached for 1 hour.
  • Intercom — REST via intercom-client SDK (contacts.search, conversations.create).
  • Clerk — inbound webhook only; outbound auth is handled by @clerk/nextjs middleware.
  • Auth model: see standards/auth-model.
  • Security model and Turnstile boundary: standards/security-model.
  • Observability — tRPC tracing middleware emits OTel spans (trpc.${type}.${path}) plus recordTrpcRequest metrics; see operations/observability.