# /api/foundry/chat (dashboard)

Source-derived dashboard endpoint contract, authentication, request validation, responses, and errors.

Source: https://docs.minds.sh/docs/api/platform/reference/dashboard--api-foundry-chat



This is the **dashboard** service route. Its origin and authentication are described in [Platform API overview](/docs/api/platform) and [Authentication](/docs/api/platform/authentication). A path shared by another service is a different endpoint.

| Property                       | Contract                                                                 |
| ------------------------------ | ------------------------------------------------------------------------ |
| Methods                        | POST, GET                                                                |
| Path                           | `/api/foundry/chat`                                                      |
| Path parameters                | None                                                                     |
| Query parameters read in route | None read directly; schema validation below may define additional fields |
| Headers read in route          | `content-type`, `x-request-id`, `cf-ray`, `x-foundry-request-id`         |

## Authentication and access [#authentication-and-access]

Dashboard session cookie through @repo/auth/server. Some auth-summary routes can redirect to sign-in; inspect the status branches below.

## Method behavior [#method-behavior]

The handler excerpt preserves field validation, response envelopes, cookie changes, and exception branches. Values returned by service helpers retain their named response type above; for passthrough routes, the upstream response is authoritative.

### POST [#post]

```typescript
export async function POST(request: NextRequest) {
  const session = await getSession();
  if (!session) {
    return NextResponse.json(
      {
        error: {
          code: 'UNAUTHENTICATED',
          message: 'Sign in at auth.l1fe.ai before using Operator chat.',
        },
      },
      { status: 401, headers: noStoreHeaders() }
    );
  }

  const apiKey = engineApiKey();
  if (!apiKey) {
    return NextResponse.json(
      {
        error: {
          code: 'FOUNDRY_UNCONFIGURED',
          message:
            'FOUNDRY_ENGINE_API_KEY is not configured on the server. Contact Minds ops.',
        },
      },
      { status: 503, headers: noStoreHeaders() }
    );
  }

  let body: Record<string, unknown>;
  try {
    body = (await request.json()) as Record<string, unknown>;
  } catch {
    return NextResponse.json(
      {
        error: {
          code: 'INVALID_JSON',
          message: 'Request body must be JSON.',
        },
      },
      { status: 400, headers: noStoreHeaders() }
    );
  }

  if (!Array.isArray(body.messages) || body.messages.length === 0) {
    return NextResponse.json(
      {
        error: {
          code: 'INVALID_MESSAGES',
          message: 'messages must be a non-empty array.',
        },
      },
      { status: 400, headers: noStoreHeaders() }
    );
  }

  const wantStream = body.stream === true;
  const messages = await withOperatorContext(
    withL1feMindsSystemMessage(body.messages as ChatMessageLike[]),
    session
  );
  const payload = {
    ...body,
    messages,
    model:
      typeof body.model === 'string' && body.model.trim()
        ? body.model
        : DEFAULT_MODEL,
    stream: wantStream,
  };

  const target = `${foundryBaseUrl()}/v1/chat/completions`;
  let upstream: Response;
  try {
    upstream = await fetch(target, {
      method: 'POST',
      headers: {
        authorization: `Bearer ${apiKey}`,
        'content-type': 'application/json',
        accept: wantStream ? 'text/event-stream' : 'application/json',
      },
      body: JSON.stringify(payload),
      cache: 'no-store',
    });
  } catch (error) {
    const message =
      error instanceof Error ? error.message : 'Foundry upstream unreachable';
    return NextResponse.json(
      {
        error: {
          code: 'FOUNDRY_UPSTREAM_UNAVAILABLE',
          message,
        },
      },
      { status: 502, headers: noStoreHeaders() }
    );
  }

  const headers = new Headers(noStoreHeaders());
  const contentType = upstream.headers.get('content-type');
  if (contentType) {
    headers.set('content-type', contentType);
  }
  // Do not forward upstream auth or cookie headers.
  const requestId =
    upstream.headers.get('x-request-id') ||
    upstream.headers.get('cf-ray') ||
    upstream.headers.get('x-foundry-request-id');
  if (requestId) {
    headers.set('x-foundry-request-id', requestId);
  }

  // Stream path: scrub SSE deltas.
  if (wantStream && upstream.body && upstream.ok) {
    headers.set('content-type', contentType || 'text/event-stream');
    return new NextResponse(sanitizeSseStream(upstream.body), {
      status: upstream.status,
      headers,
    });
  }

  // Non-stream JSON: scrub assistant content before returning to UI.
  if (
    upstream.body &&
    contentType &&
    contentType.includes('application/json')
  ) {
    try {
      const rawText = await upstream.text();
      let parsed: unknown = {};
      try {
        parsed = rawText ? JSON.parse(rawText) : {};
      } catch {
        // Upstream returned non-JSON; still strip think tags from raw body.
        return new NextResponse(sanitizeAssistantText(rawText), {
          status: upstream.status,
          headers,
        });
      }
      const cleaned = sanitizeFoundryCompletionJson(parsed);
      return NextResponse.json(cleaned, {
        status: upstream.status,
        headers: noStoreHeaders(),
      });
    } catch (error) {
      const message =
        error instanceof Error
          ? error.message
          : 'Failed to sanitize Foundry response';
      return NextResponse.json(
        {
          error: {
            code: 'FOUNDRY_SANITIZE_FAILED',
            message,
          },
        },
        { status: 502, headers: noStoreHeaders() }
      );
    }
  }

  return new NextResponse(upstream.body, {
    status: upstream.status,
    headers,
  });
}
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:148`.

### GET [#get]

```typescript
export async function GET() {
  return NextResponse.json(
    {
      error: {
        code: 'METHOD_NOT_ALLOWED',
        message: 'Use POST /api/foundry/chat with an OpenAI-compatible body.',
      },
    },
    { status: 405, headers: noStoreHeaders() }
  );
}
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:314`.

## Response and error branches [#response-and-error-branches]

Literal HTTP statuses in the route: 400, 401, 405, 502, 503. Shared management error classes are defined in [Errors and responses](/docs/api/platform/errors).

```typescript
NextResponse.json(
      {
        error: {
          code: 'UNAUTHENTICATED',
          message: 'Sign in at auth.l1fe.ai before using Operator chat.',
        },
      },
      { status: 401, headers: noStoreHeaders() }
    )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:151`.

```typescript
NextResponse.json(
      {
        error: {
          code: 'FOUNDRY_UNCONFIGURED',
          message:
            'FOUNDRY_ENGINE_API_KEY is not configured on the server. Contact Minds ops.',
        },
      },
      { status: 503, headers: noStoreHeaders() }
    )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:164`.

```typescript
request.json()
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:178`.

```typescript
NextResponse.json(
      {
        error: {
          code: 'INVALID_JSON',
          message: 'Request body must be JSON.',
        },
      },
      { status: 400, headers: noStoreHeaders() }
    )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:180`.

```typescript
NextResponse.json(
      {
        error: {
          code: 'INVALID_MESSAGES',
          message: 'messages must be a non-empty array.',
        },
      },
      { status: 400, headers: noStoreHeaders() }
    )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:192`.

```typescript
NextResponse.json(
      {
        error: {
          code: 'FOUNDRY_UPSTREAM_UNAVAILABLE',
          message,
        },
      },
      { status: 502, headers: noStoreHeaders() }
    )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:234`.

```typescript
NextResponse.json(cleaned, {
        status: upstream.status,
        headers: noStoreHeaders(),
      })
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:287`.

```typescript
NextResponse.json(
        {
          error: {
            code: 'FOUNDRY_SANITIZE_FAILED',
            message,
          },
        },
        { status: 502, headers: noStoreHeaders() }
      )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:296`.

```typescript
NextResponse.json(
    {
      error: {
        code: 'METHOD_NOT_ALLOWED',
        message: 'Use POST /api/foundry/chat with an OpenAI-compatible body.',
      },
    },
    { status: 405, headers: noStoreHeaders() }
  )
```

Source: `minds-ui/apps/app/app/api/foundry/chat/route.ts:315`.

## Service dependencies [#service-dependencies]

* `minds-ui/apps/app/lib/chat/operator-context.ts`
* `minds-ui/apps/app/lib/organizations.ts`

## Evidence [#evidence]

Generated from the local source snapshot on 2026-09-12. This page documents implemented code and does not certify a deployed service, permissions configuration, or successful provider operation.

Source file: `minds-ui/apps/app/app/api/foundry/chat/route.ts`. SHA-256: `2d518ad13d432790c13dd29ae4eccc2f32f38f545e9c31207a82f64554372af0`.
