Applications
Applications are custom frontend interfaces that can be uploaded and integrated into Forra assistants. They provide a tailored user experience for specific workflows while leveraging Forra's backend capabilities through the Forra Chat SDK.
Quick Start
The fastest way to get started is with the Forra CLI:
forracli apps init -d my-app
cd my-app/micro-app
bun install
forracli apps deploy -f ../assistants.json
make dev
Open Forra and find your created assistant - the template app replaces the chat interface.
Production Deployment: Remove the ui_url (dev) section from assistants.json when ready for other users.
Advanced Overview
An Application is essentially a web application that:
- Uses the @mirego/forra-api SDK to interact with Forra's API
- Uses the @mirego/forra-react SDK to use Forra's reusable components
- Can create and manage conversations (sessions)
- Can read and write user data for session persistence
- Can execute custom functions defined in the assistant
Core Concepts
Access Token
When an Application is loaded within Forra, it automatically receives an access token that provides access to: - Conversation Management: Create new conversations and manage existing ones - User Data: Store and retrieve custom session information - Custom Functions: Execute backend functions uploaded to the assistant
Session Management
Each Application instance can run within a conversation context, allowing you to: - Persist state across user interactions - Track progress through multi-step workflows - Store custom data specific to the session
Building from Scratch
1. Setup Your Project
Create a web application using any framework that outputs an index.html-based bundle. React with Vite is recommended.
2. Wrap Your App with ScoutApp
import { ScoutApp } from '@mirego/forra-react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ScoutApp>
<App />
</ScoutApp>
</StrictMode>
);
3. Access Forra Context
import { ScoutAppContext } from '@mirego/forra-react';
import { useContext } from 'react';
const App = () => {
const {
conversation_id,
assistant_id,
language,
redirectToNewConversation,
onConversationCreated,
redirectToConversation
} = useContext(ScoutAppContext);
// Your app logic here
return <div>Your application content</div>;
};
Key SDK Functions
Conversation Management
Create a New Conversation
import { createConversation } from '@mirego/forra-react';
const newConversation = await createConversation({
title: 'My Workflow Session',
payload: [],
assistant_id,
user_data: { step: 'initial', customData: 'value' }
});
Query Conversation Data
import { useConversationQuery } from '@mirego/forra-react';
const conversationQuery = useConversationQuery({
conversationId: conversation_id,
refetchInterval: 1000 // Poll every second if needed
});
const userData = conversationQuery.data?.user_data;
Update User Data
import { updateConversationUserData } from '@mirego/forra-api';
await updateConversationUserData(conversation_id, {
step: 'processing',
progress: 50,
customField: 'updated value'
});
Update a Single User Data Key
import { updateConversationUserDataKey } from '@mirego/forra-api';
await updateConversationUserDataKey(conversation_id, 'step', 'processing');
Only the specified key is updated — other keys in user_data are left unchanged.
Execute Custom Functions
import { executeAssistantCustomFunction } from '@mirego/forra-api';
await executeAssistantCustomFunction(
assistant_id,
'my_custom_function_name',
{
input_parameter: 'value',
another_param: 123
},
conversation_id
);
File Handling
import {
getSignedUploadUrl,
uploadFile,
fetchSignedUrl
} from '@mirego/forra-api';
// Upload a file
const uploadUrl = await getSignedUploadUrl(filename, filesize);
await uploadFile(uploadUrl.data.url, file);
// Download a file
const downloadUrl = await fetchSignedUrl(protectedFilePath);
const response = await axios.get(downloadUrl.data.url, {
responseType: 'blob'
});
UI Components
The SDK provides pre-built UI components that follow Forra's design system:
import {
MicroAppHeader,
MicroAppNewConversationButton,
MicroAppStepsContainer,
MicroAppStep,
MicroAppFileUpload,
MicroAppFileInfo,
Button,
Input
} from '@mirego/forra-react';
// Header with logo and title
<MicroAppHeader
AppLogo={() => <img src="your-logo.png" />}
title="My Application"
subtitle="Description of functionality"
/>
// Step-based workflow
<MicroAppStepsContainer>
<MicroAppStep
state="completed" // 'idle' | 'loading' | 'completed' | 'fail'
title="Step 1"
errorMessage="Error message if failed"
>
Step content here
</MicroAppStep>
</MicroAppStepsContainer>
// File operations
<MicroAppFileUpload onUpload={handleFileUpload} />
<MicroAppFileInfo
filename="output.txt"
onDownload={handleDownload}
/>
Example Implementation
Here's a simplified example of an application that processes a URL through multiple steps:
import {
ScoutAppContext,
useConversationQuery,
createConversation,
executeAssistantCustomFunction,
MicroAppHeader,
MicroAppStepsContainer,
MicroAppStep
} from '@mirego/forra-react';
import { useContext, useState, useEffect } from 'react';
interface UserData {
step: 'idle' | 'processing' | 'completed';
inputUrl?: string;
result?: string;
error?: string;
}
const App = () => {
const { conversation_id, assistant_id, redirectToNewConversation } =
useContext(ScoutAppContext);
const [inputUrl, setInputUrl] = useState('');
const conversationQuery = useConversationQuery({
conversationId: conversation_id,
refetchInterval: conversation_id ? 1000 : undefined
});
const userData = conversationQuery.data?.user_data as UserData;
const processUrl = async () => {
const conversation = await createConversation({
title: `Processing ${inputUrl}`,
payload: [],
assistant_id,
user_data: { step: 'processing', inputUrl }
});
await executeAssistantCustomFunction(
assistant_id,
'process_url_function',
{ url: inputUrl },
conversation.data.id
);
};
return (
<div>
{conversation_id && (
<MicroAppNewConversationButton
onClick={redirectToNewConversation}
/>
)}
<MicroAppHeader
title="URL Processor"
subtitle="Process URLs with AI"
/>
{!userData || userData.step === 'idle' ? (
<div>
<input
value={inputUrl}
onChange={(e) => setInputUrl(e.target.value)}
placeholder="Enter URL"
/>
<button onClick={processUrl}>Process</button>
</div>
) : (
<MicroAppStepsContainer>
<MicroAppStep
state={userData.step === 'processing' ? 'loading' : 'completed'}
title="Processing URL"
errorMessage={userData.error}
>
{userData.result && <p>{userData.result}</p>}
</MicroAppStep>
</MicroAppStepsContainer>
)}
</div>
);
};