Nodes:
name: OpenAI1
type: @n8n/n8n-nodes-langchain.openAi
parameters: text: ={{ 
     JSON.parse($('Insert Transcription Part').item.json.dialog)
        .filter(item => item.date_updated && new Date(item.date_updated) >= new Date($('Insert Transcription Part').item.json.date_updated))
        .sort((a, b) => a.order - b.order)
        .map(item => `${item.words}\n${item.speaker}`)
        .join('\n\n')
}}
memory: threadId
prompt: define
resource: assistant
threadId: ={{ $json.thread_id }}
assistantId: __rl: yes
mode: list
value: asst_D5t6bNnNpenmfC7PmvywMqyR
cachedResultName: 5minAI - Realtime Agent
name: Insert Transcription Part
type: n8n-nodes-base.postgres
parameters: query: UPDATE public.data
SET output = jsonb_set(
    output,
    '{dialog}', 
    (
        COALESCE(
            (output->'dialog')::jsonb, 
            '[]'::jsonb  -- Initialize as empty array if dialog does not exist
        ) || jsonb_build_object(
            'order', (COALESCE(jsonb_array_length(output->'dialog'), 0) + 1),  -- Calculate the next order
            'words', '{{ $('Webhook2').item.json.body.data.transcript.words.map(word => word.text.replace(/'/g, "''")).join(" ") }}',
            'speaker', '{{ $('Webhook2').item.json.body.data.transcript.speaker }}',
            'language', '{{ $('Webhook2').item.json.body.data.transcript.language }}',
            'speaker_id', ('{{ $('Webhook2').item.json.body.data.transcript.speaker_id }}')::int,
  'date_updated', to_jsonb('{{ $now }}'::text)
        )
    )
)
WHERE input->>'recall_bot_id' = $1
Returning input->>'openai_thread_id' as thread_id;
options: queryReplacement: ={{ $('Scenario 2 Start - Webhook').item.json.body.data.bot_id }}
operation: executeQuery
name: Create Note
type: n8n-nodes-base.postgresTool
parameters: query: UPDATE public.data
SET output = jsonb_set(
    output,
    '{notes}', 
    (
        COALESCE(
            (output->'notes')::jsonb, 
            '[]'::jsonb  -- Initialize as empty array if dialog does not exist
        ) || jsonb_build_object(
            'order', (COALESCE(jsonb_array_length(output->'notes'), 0) + 1),  -- Calculate the next order
            'text', '{{ $fromAI("note","Text of note.") }}'
        )
    )
)
WHERE input->>'recall_bot_id' = $1
options: queryReplacement: ={{ $('Scenario 2 Start - Webhook').item.json.body.data.bot_id }}
operation: executeQuery
descriptionType: manual
toolDescription: Create note record.
name: Create Recall bot
type: n8n-nodes-base.httpRequest
parameters: url: https://us-west-2.recall.ai/api/v1/bot
method: POST
jsonBody: ={
  "meeting_url":"{{ $json.meeting_url }}",
  "transcription_options": {
    "provider": "assembly_ai"
  }
,
"real_time_transcription": {
    "destination_url": "https://n8n.lowcoding.dev/webhook/d074ca1e-52f9-47af-8587-8c24d431f9cd"
  },
"automatic_leave": {
  "silence_detection": {
    "timeout": 300, 
    "activate_after": 600
  },
  "bot_detection": {
    "using_participant_events": {
      "timeout": 600, 
      "activate_after": 1200
    }
  },
  "waiting_room_timeout": 600,
  "noone_joined_timeout": 600,
  "everyone_left_timeout": 2,
  "in_call_not_recording_timeout": 600,
  "recording_permission_denied_timeout": 600
}
}
sendBody: yes
specifyBody: json
authentication: genericCredentialType
genericAuthType: httpHeaderAuth
name: Create OpenAI thread
type: n8n-nodes-base.httpRequest
parameters: url: https://api.openai.com/v1/threads
method: POST
sendHeaders: yes
authentication: predefinedCredentialType
headerParameters: parameters: name: OpenAI-Beta
value: assistants=v2
nodeCredentialType: openAiApi
name: Create data record
type: n8n-nodes-base.supabase
parameters: tableId: data
fieldsUi: fieldValues: ={{ {"openai_thread_id": $('Create OpenAI thread').item.json.id, "recall_bot_id": $('Create Recall bot').item.json.id, "meeting_url":$('Webhook').item.json.body.meeting_url } }}
={{ {"dialog":[]} }}
name: Sticky Note9
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 330.5152611046425
height: 239.5888196628349
content: ### ... or watch set up video [10 min]
[![Youtube Thumbnail](https://res.cloudinary.com/de9jgixzm/image/upload/v1739773273/Youtube%20Thumbs/Realtim%20AI%20Agent.png)](https://www.youtube.com/watch?v=rtaX6BMiTeo)

name: Sticky Note7
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 636
height: 657
content: ![5min Logo](https://res.cloudinary.com/de9jgixzm/image/upload/v1739773200/Skool%20Assets/ejm3hqnvhgwpnu2fv92s.png)
## AI Agent for realtime insights on meetings
**Made by [Mark Shcherbakov](https://www.linkedin.com/in/marklowcoding/) from community [5minAI](https://www.skool.com/5minai)**

Transcribing meetings manually can be tedious and prone to error. This workflow automates the transcription process in real-time, ensuring that key discussions and decisions are accurately captured and easily accessible for later review, thus enhancing productivity and clarity in communications.

The workflow employs an AI-powered assistant to join virtual meetings and capture discussions through real-time transcription. Key functionalities include:
- Automatic joining of meetings on platforms like Zoom, Google Meet, and others with the ability to provide real-time transcription.
- Integration with transcription APIs (e.g., AssemblyAI) to deliver seamless and accurate capture of dialogue.
- Structuring and storing transcriptions efficiently in a database for easy retrieval and analysis.

1. **Real-Time Transcription**: The assistant captures audio during meetings and transcribes it in real-time, allowing participants to focus on discussions.
2. **Keyword Recognition**: Key phrases can trigger specific actions, such as noting important points or making prompts to the assistant.
3. **Structured Data Management**: The assistant maintains a database of transcriptions linked to meeting details for organized storage and quick access later.
name: Sticky Note6
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 280
height: 626
content: ### Set up steps

#### Preparation

1. **Create Recall.ai API key**
2. **Setup Supabase account and table**
```
create table
  public.data (
    id uuid not null default gen_random_uuid (),
    date_created timestamp with time zone not null default (now() at time zone 'utc'::text),
    input jsonb null,
    output jsonb null,
    constraint data_pkey primary key (id),
  ) tablespace pg_default;

```
3. **Create OpenAI API key**

#### Development

1. **Bot Creation**: 
   - Use a node to create the bot that will join meetings. Provide the meeting URL and set transcription options within the API request.

2. **Authentication**: 
   - Configure authentication settings via a Bearer token for interacting with your transcription service.

3. **Webhook Setup**: 
   - Create a webhook to receive real-time transcription updates, ensuring timely data capture during meetings.

4. **Join Meeting**: 
   - Set the bot to join the specified meeting and actively listen to capture conversations.

5. **Transcription Handling**: 
   - Combine transcription fragments into cohesive sentences and manage dialog arrays for coherence.

6. **Trigger Actions on Keywords**: 
   - Set up keyword recognition that can initiate requests to the OpenAI API for additional interactions based on captured dialogue.

7. **Output and Summary Generation**: 
   - Produce insights and summary notes from the transcriptions that can be stored back into the database for future reference.
name: Scenario 1 Start - Edit Fields
type: n8n-nodes-base.set
parameters: assignments: assignments: name: meeting_url
type: string
value: https://meet.google.com/iix-vrav-kuc
name: Scenario 2 Start - Webhook
type: n8n-nodes-base.webhook
parameters: path: d074ca1e-52f9-47af-8587-8c24d431f9cd
httpMethod: POST
name: If Jimmy word
type: n8n-nodes-base.if
parameters: conditions: options: version: 2
caseSensitive: yes
typeValidation: strict
combinator: and
conditions: operator: type: string
operation: contains
leftValue: ={{ $('Scenario 2 Start - Webhook').item.json.body.data.transcript.words.map(word => word.text.replace(/'/g, "''")).join(" ") }}
rightValue: =Jimmy
name: Sticky Note
type: n8n-nodes-base.stickyNote
parameters: width: 920
height: 400
content: ## Scenario 1


name: Sticky Note1
type: n8n-nodes-base.stickyNote
parameters: width: 1020
height: 420
content: ## Scenario 2


name: Sticky Note2
type: n8n-nodes-base.stickyNote
parameters: color: 3
width: 270
height: 80
content: ### Replace Supabase credentials
name: Sticky Note3
type: n8n-nodes-base.stickyNote
parameters: color: 3
width: 200
height: 80
content: ### Replace server location


name: Sticky Note4
type: n8n-nodes-base.stickyNote
parameters: color: 4
width: 270
height: 80
content: ### Replace OpenAI credentials
name: Sticky Note5
type: n8n-nodes-base.stickyNote
parameters: color: 3
width: 290
height: 80
content: ### Replace credentials
name: Sticky Note8
type: n8n-nodes-base.stickyNote
parameters: color: 3
width: 200
height: 80
content: ### Replace meeting url



Connections:
Create Note: ai_tool: node: OpenAI1
type: ai_tool
If Jimmy word: main: node: OpenAI1
type: main
Create Recall bot: main: node: Create OpenAI thread
type: main
Create OpenAI thread: main: node: Create data record
type: main
Insert Transcription Part: main: node: If Jimmy word
type: main
Scenario 2 Start - Webhook: main: node: Insert Transcription Part
type: main
Scenario 1 Start - Edit Fields: main: node: Create Recall bot
type: main