> ## Documentation Index
> Fetch the complete documentation index at: https://synthetiq.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Procedures reference

> tRPC procedure types, context object, metadata, streaming, and router structure

## Procedure types

```typescript theme={null}
import { router, publicProcedure, scopedProcedure } from '@synthetiq/app-framework/server';
```

| Type                                 | Authentication                     | Use case                      |
| ------------------------------------ | ---------------------------------- | ----------------------------- |
| `publicProcedure`                    | None (`ctx.userId` may be null)    | Health checks, public data    |
| `scopedProcedure([])`                | Required (`ctx.userId` guaranteed) | Any authenticated user        |
| `scopedProcedure(['scope'])`         | Required + scope check             | Users with specific scopes    |
| `scopedProcedure(['a', 'b'])`        | Required + all scopes (AND)        | Users with multiple scopes    |
| `scopedProcedure(['a', 'b'], 'any')` | Required + any scope (OR)          | Users with at least one scope |

### publicProcedure example

```typescript theme={null}
export const utilsRouter = router({
  healthCheck: publicProcedure
    .meta({ description: 'Check if the app is running' })
    .query(() => ({ status: 'ok' })),
});
```

## Context object

| Property                | Type                     | Description                              |
| ----------------------- | ------------------------ | ---------------------------------------- |
| `ctx.userId`            | `string`                 | Authenticated user's ID                  |
| `ctx.userName`          | `string \| null`         | User's display name                      |
| `ctx.userEmail`         | `string \| null`         | User's email                             |
| `ctx.userPicture`       | `string \| null`         | User's profile picture URL               |
| `ctx.userEmailVerified` | `boolean \| null`        | Email verification status                |
| `ctx.currentOrgId`      | `string \| null`         | User's selected organization ID          |
| `ctx.userOrgIds`        | `string[]`               | All organization IDs the user belongs to |
| `ctx.db`                | `PrismaClient`           | Prisma client with RLS context           |
| `ctx.appSettings`       | `Record<string, string>` | App settings from `config.ts`            |

## Metadata

Every procedure must include `.meta({ description })`. The build fails if metadata is missing.

| Field         | Required | Description                                                       |
| ------------- | -------- | ----------------------------------------------------------------- |
| `description` | Yes      | Used by AI agent, HTTP API docs, MCP server, and `/docs/api` page |
| `mcp`         | No       | Set to `false` to exclude from MCP exposure                       |

```typescript theme={null}
internalSync: scopedProcedure([])
  .meta({ description: 'Internal data sync', mcp: false })
  .mutation(...)
```

## Streaming procedures

Procedures can return async generators for streaming responses:

```typescript theme={null}
streamUpdates: scopedProcedure([])
  .meta({ description: 'Stream real-time updates' })
  .input(z.object({ topic: z.string() }))
  .query(async function* ({ input }) {
    const client = new MyServiceClient();
    for await (const update of client.subscribe(input.topic)) {
      yield update;
    }
  }),
```

## Router structure

```typescript theme={null}
// src/server/router.ts
import { setAppRouter } from "./init";
import { router, adminRouter, userRouter, utilsRouter, aiAgentRouter,
         docsRouter, servicesRouter, oauthAdminRouter, oauthAppsRouter,
         workflowAdminRouter, logsRouter, metricsRouter } from "@synthetiq/app-framework/server";
import { tasksRouter } from "./routes/tasks";

export const appRouter = router({
  // System routers (always include all)
  utils: utilsRouter,
  user: userRouter,
  ai: aiAgentRouter,
  admin: adminRouter,
  docs: docsRouter,
  services: servicesRouter,
  oauthAdmin: oauthAdminRouter,
  oauthApps: oauthAppsRouter,
  workflowAdmin: workflowAdminRouter,
  logs: logsRouter,
  metrics: metricsRouter,
  // App routers
  tasks: tasksRouter,
});

setAppRouter(appRouter);

export type AppRouter = typeof appRouter;
```

Procedures must be defined in `src/server/routes/*.ts`. The router validator blocks inline procedure definitions in `router.ts`.

## Frontend usage

```typescript theme={null}
// Direct call
const tasks = await trpc.tasks.getMyTasks.query();

// React Query hooks
const { data } = trpcReact.tasks.getMyTasks.useQuery();
const mutation = trpcReact.tasks.createTask.useMutation({
  onSuccess: () => utils.tasks.getMyTasks.invalidate(),
});
```

<Tip>After a mutation, remember to invalidate related queries (as shown with `utils.tasks.getMyTasks.invalidate()`) so the UI reflects the latest data without a hard refresh.</Tip>
