API referencePlatform APIEndpoint reference
GUIDE & REFERENCE

/api/foundry/chat (dashboard)

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

This is the dashboard service route. Its origin and authentication are described in Platform API overview and Authentication. A path shared by another service is a different endpoint.

PropertyContract
MethodsPOST, GET
Path/api/foundry/chat
Path parametersNone
Query parameters read in routeNone read directly; schema validation below may define additional fields
Headers read in routecontent-type, x-request-id, cf-ray, x-foundry-request-id

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

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

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

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

Literal HTTP statuses in the route: 400, 401, 405, 502, 503. Shared management error classes are defined in Errors and responses.

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.

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.

request.json()

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

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.

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.

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

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

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

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

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

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

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

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

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.

On this page