[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F7F992W22YMBZZZE3QFQDP":3,"skill-same:01M6F7F992W22YMBZZZE3QFQDP":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":59,"ownerHtmlUrl":60,"ownerBlogUrl":6,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6F7F992W22YMBZZZE3QFQDP","assemblyai-reference-architecture",null,"Implement AssemblyAI reference architecture with best-practice project layout.\nUse when designing new AssemblyAI transcription services, reviewing project structure,\nor building production-grade speech-to-text applications.\nTrigger with phrases like \"assemblyai architecture\", \"assemblyai best practices\",\n\"assemblyai project structure\", \"how to organize assemblyai\", \"assemblyai design\".","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fassemblyai-reference-architecture\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"reference","assemblyai reference architecture","assemblyai","architecture","organize","project structure","how to","trigger","io","layout","project","structure","code","design","use","read","text","how","applications","speech","2026-11-10 10:12:30",false,"---\nname: assemblyai-reference-architecture\ndescription: |\n  Implement AssemblyAI reference architecture with best-practice project layout.\n  Use when designing new AssemblyAI transcription services, reviewing project structure,\n  or building production-grade speech-to-text applications.\n  Trigger with phrases like \"assemblyai architecture\", \"assemblyai best practices\",\n  \"assemblyai project structure\", \"how to organize assemblyai\", \"assemblyai design\".\nallowed-tools: Read, Grep\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ntags: [saas, ai, speech-to-text, assemblyai, transcription, architecture]\ncompatible-with: claude-code\n---\n\n# AssemblyAI Reference Architecture\n\n## Overview\nProduction-ready architecture for AssemblyAI-powered transcription services with layered design, webhook-driven processing, and LeMUR analysis pipelines.\n\n## Prerequisites\n- Understanding of layered architecture\n- `assemblyai` npm package\n- TypeScript project setup\n- Database for transcript storage\n\n## Project Structure\n\n```\nmy-transcription-service\u002F\n├── src\u002F\n│   ├── assemblyai\u002F\n│   │   ├── client.ts              # Singleton client\n│   │   ├── transcription.ts       # Transcription service\n│   │   ├── streaming.ts           # Streaming service\n│   │   ├── lemur.ts               # LeMUR analysis service\n│   │   └── types.ts               # Domain types\n│   ├── api\u002F\n│   │   ├── transcribe.ts          # POST \u002Fapi\u002Ftranscribe\n│   │   ├── transcripts.ts         # GET \u002Fapi\u002Ftranscripts\u002F:id\n│   │   ├── streaming-token.ts     # GET \u002Fapi\u002Fstreaming-token\n│   │   └── webhooks\u002F\n│   │       └── assemblyai.ts      # POST \u002Fwebhooks\u002Fassemblyai\n│   ├── services\u002F\n│   │   ├── audio-processor.ts     # Audio validation & preprocessing\n│   │   └── transcript-store.ts    # Database storage\n│   ├── jobs\u002F\n│   │   └── batch-transcriber.ts   # Background batch processing\n│   └── config.ts\n├── tests\u002F\n│   ├── unit\u002F\n│   │   ├── transcription.test.ts\n│   │   └── lemur.test.ts\n│   └── integration\u002F\n│       └── assemblyai.test.ts\n└── package.json\n```\n\n## Architecture Layers\n\n```\n┌──────────────────────────────────────────────────────┐\n│                    API Layer                          │\n│  Transcribe endpoint, Webhook handler, Stream token  │\n├──────────────────────────────────────────────────────┤\n│                  Service Layer                        │\n│  TranscriptionService, LeMURService, AudioProcessor  │\n├──────────────────────────────────────────────────────┤\n│                AssemblyAI SDK Layer                   │\n│  client.transcripts, client.streaming, client.lemur  │\n├──────────────────────────────────────────────────────┤\n│               Infrastructure Layer                    │\n│  Database, Redis Cache, Job Queue, Monitoring        │\n└──────────────────────────────────────────────────────┘\n```\n\n## Instructions\n\n### Step 1: Client Layer\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Fclient.ts\nimport { AssemblyAI } from 'assemblyai';\n\nlet client: AssemblyAI | null = null;\n\nexport function getClient(): AssemblyAI {\n  if (!client) {\n    client = new AssemblyAI({\n      apiKey: process.env.ASSEMBLYAI_API_KEY!,\n    });\n  }\n  return client;\n}\n```\n\n### Step 2: Transcription Service\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Ftranscription.ts\nimport { type Transcript, type TranscriptParams } from 'assemblyai';\nimport { getClient } from '.\u002Fclient';\n\nexport interface TranscriptionOptions {\n  speakerLabels?: boolean;\n  sentimentAnalysis?: boolean;\n  entityDetection?: boolean;\n  piiRedaction?: boolean;\n  webhookUrl?: string;\n  model?: 'best' | 'nano';\n}\n\nexport class TranscriptionService {\n  private client = getClient();\n\n  \u002F\u002F Async transcription with webhook (production pattern)\n  async submitForTranscription(\n    audio: string,\n    options: TranscriptionOptions = {}\n  ): Promise\u003C{ transcriptId: string; status: string }> {\n    const params: TranscriptParams = {\n      audio,\n      speech_model: options.model ?? 'best',\n      speaker_labels: options.speakerLabels ?? false,\n      sentiment_analysis: options.sentimentAnalysis ?? false,\n      entity_detection: options.entityDetection ?? false,\n      redact_pii: options.piiRedaction ?? false,\n    };\n\n    if (options.webhookUrl) {\n      params.webhook_url = options.webhookUrl;\n      params.webhook_auth_header_name = 'X-Webhook-Secret';\n      params.webhook_auth_header_value = process.env.ASSEMBLYAI_WEBHOOK_SECRET!;\n    }\n\n    const transcript = options.webhookUrl\n      ? await this.client.transcripts.submit(params)\n      : await this.client.transcripts.transcribe(params);\n\n    return { transcriptId: transcript.id, status: transcript.status };\n  }\n\n  \u002F\u002F Get completed transcript with all data\n  async getTranscript(id: string): Promise\u003CTranscript> {\n    return this.client.transcripts.get(id);\n  }\n\n  \u002F\u002F Delete for GDPR compliance\n  async deleteTranscript(id: string): Promise\u003Cvoid> {\n    await this.client.transcripts.delete(id);\n  }\n\n  \u002F\u002F Batch processing\n  async batchTranscribe(audioUrls: string[], options: TranscriptionOptions = {}) {\n    const PQueue = (await import('p-queue')).default;\n    const queue = new PQueue({ concurrency: 5 });\n\n    return Promise.all(\n      audioUrls.map(url =>\n        queue.add(() => this.submitForTranscription(url, options))\n      )\n    );\n  }\n}\n```\n\n### Step 3: LeMUR Analysis Service\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Flemur.ts\nimport { getClient } from '.\u002Fclient';\n\nexport class LeMURService {\n  private client = getClient();\n\n  async summarize(transcriptIds: string[], context?: string) {\n    const { response } = await this.client.lemur.summary({\n      transcript_ids: transcriptIds,\n      context,\n      answer_format: 'bullet points',\n    });\n    return response;\n  }\n\n  async extractActionItems(transcriptIds: string[]) {\n    const { response } = await this.client.lemur.actionItems({\n      transcript_ids: transcriptIds,\n    });\n    return response;\n  }\n\n  async askQuestions(transcriptIds: string[], questions: string[]) {\n    const { response } = await this.client.lemur.questionAnswer({\n      transcript_ids: transcriptIds,\n      questions: questions.map(q => ({ question: q })),\n    });\n    return response;\n  }\n\n  async customTask(transcriptIds: string[], prompt: string) {\n    const { response } = await this.client.lemur.task({\n      transcript_ids: transcriptIds,\n      prompt,\n    });\n    return response;\n  }\n}\n```\n\n### Step 4: Streaming Service\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Fstreaming.ts\nimport { getClient } from '.\u002Fclient';\n\nexport class StreamingService {\n  private client = getClient();\n\n  async createToken(expiresInSeconds = 300) {\n    return this.client.streaming.createTemporaryToken({\n      expires_in_seconds: expiresInSeconds,\n    });\n  }\n\n  createTranscriber(options: {\n    sampleRate?: number;\n    model?: string;\n    wordBoost?: string[];\n  } = {}) {\n    return this.client.streaming.createService({\n      speech_model: (options.model as any) ?? 'nova-3',\n      sample_rate: options.sampleRate ?? 16000,\n      word_boost: options.wordBoost,\n    });\n  }\n}\n```\n\n### Step 5: Webhook Handler\n\n```typescript\n\u002F\u002F src\u002Fapi\u002Fwebhooks\u002Fassemblyai.ts\nimport { TranscriptionService } from '..\u002F..\u002Fassemblyai\u002Ftranscription';\nimport { LeMURService } from '..\u002F..\u002Fassemblyai\u002Flemur';\n\nconst transcription = new TranscriptionService();\nconst lemur = new LeMURService();\n\nexport async function handleWebhook(req: Request): Promise\u003CResponse> {\n  \u002F\u002F Verify auth\n  const secret = req.headers.get('x-webhook-secret');\n  if (secret !== process.env.ASSEMBLYAI_WEBHOOK_SECRET) {\n    return new Response('Unauthorized', { status: 401 });\n  }\n\n  const { transcript_id, status } = await req.json();\n\n  \u002F\u002F Respond immediately\n  const response = new Response(JSON.stringify({ received: true }), { status: 200 });\n\n  \u002F\u002F Process asynchronously\n  if (status === 'completed') {\n    const transcript = await transcription.getTranscript(transcript_id);\n\n    \u002F\u002F Auto-analyze with LeMUR\n    const summary = await lemur.summarize([transcript_id]);\n    const actionItems = await lemur.extractActionItems([transcript_id]);\n\n    \u002F\u002F Store results in your database\n    await storeResults(transcript, summary, actionItems);\n  }\n\n  return response;\n}\n```\n\n### Step 6: Data Flow\n\n```\nUser uploads audio\n        │\n        ▼\n┌─────────────────┐\n│  API: \u002Ftranscribe│\n│  submit() + URL  │\n└────────┬────────┘\n         │\n         ▼\n┌─────────────────┐\n│  AssemblyAI API  │  ← Processes audio (seconds to minutes)\n│  (async queue)   │\n└────────┬────────┘\n         │ webhook POST\n         ▼\n┌─────────────────┐\n│ Webhook Handler  │\n│ get() transcript │\n└────────┬────────┘\n         │\n    ┌────┴────┐\n    ▼         ▼\n┌────────┐ ┌────────┐\n│ Store  │ │ LeMUR  │  ← Auto-summarize, extract action items\n│ in DB  │ │ Analyze│\n└────────┘ └────────┘\n```\n\n## Configuration\n\n```typescript\n\u002F\u002F src\u002Fconfig.ts\nexport const config = {\n  assemblyai: {\n    model: (process.env.ASSEMBLYAI_MODEL ?? 'best') as 'best' | 'nano',\n    webhookUrl: process.env.ASSEMBLYAI_WEBHOOK_URL,\n    webhookSecret: process.env.ASSEMBLYAI_WEBHOOK_SECRET,\n    defaultFeatures: {\n      speakerLabels: true,\n      sentimentAnalysis: false,\n      entityDetection: false,\n      piiRedaction: process.env.NODE_ENV === 'production',\n    },\n    streaming: {\n      tokenExpiry: 300,\n      sampleRate: 16000,\n      model: 'nova-3',\n    },\n    batch: {\n      concurrency: 5,\n      retries: 3,\n    },\n  },\n};\n```\n\n## Output\n- Layered architecture with clear separation of concerns\n- Transcription service with webhook-based async processing\n- LeMUR analysis pipeline auto-triggered on completion\n- Streaming service with temporary token management\n- Batch processing with concurrency control\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Circular dependencies | Wrong layer boundaries | Services depend on SDK layer, never on API layer |\n| Webhook missed | Processing took too long | Return 200 immediately, process async |\n| LeMUR timeout | Too many transcripts | Batch transcript_ids in groups of 10 |\n| Streaming disconnect | Network interruption | Implement reconnection in StreamingService |\n\n## Resources\n- [AssemblyAI Documentation](https:\u002F\u002Fwww.assemblyai.com\u002Fdocs)\n- [AssemblyAI Node SDK](https:\u002F\u002Fgithub.com\u002FAssemblyAI\u002Fassemblyai-node-sdk)\n- [AssemblyAI API Reference](https:\u002F\u002Fwww.assemblyai.com\u002Fdocs\u002Fapi-reference\u002Foverview)\n- [AssemblyAI Blog — Best Practices](https:\u002F\u002Fwww.assemblyai.com\u002Fblog)\n\n## Next Steps\nFor getting started quickly, see `assemblyai-hello-world`.\n\n","# AssemblyAI Reference Architecture\n\n## Overview\nProduction-ready architecture for AssemblyAI-powered transcription services with layered design, webhook-driven processing, and LeMUR analysis pipelines.\n\n## Prerequisites\n- Understanding of layered architecture\n- `assemblyai` npm package\n- TypeScript project setup\n- Database for transcript storage\n\n## Project Structure\n\n```\nmy-transcription-service\u002F\n├── src\u002F\n│   ├── assemblyai\u002F\n│   │   ├── client.ts              # Singleton client\n│   │   ├── transcription.ts       # Transcription service\n│   │   ├── streaming.ts           # Streaming service\n│   │   ├── lemur.ts               # LeMUR analysis service\n│   │   └── types.ts               # Domain types\n│   ├── api\u002F\n│   │   ├── transcribe.ts          # POST \u002Fapi\u002Ftranscribe\n│   │   ├── transcripts.ts         # GET \u002Fapi\u002Ftranscripts\u002F:id\n│   │   ├── streaming-token.ts     # GET \u002Fapi\u002Fstreaming-token\n│   │   └── webhooks\u002F\n│   │       └── assemblyai.ts      # POST \u002Fwebhooks\u002Fassemblyai\n│   ├── services\u002F\n│   │   ├── audio-processor.ts     # Audio validation & preprocessing\n│   │   └── transcript-store.ts    # Database storage\n│   ├── jobs\u002F\n│   │   └── batch-transcriber.ts   # Background batch processing\n│   └── config.ts\n├── tests\u002F\n│   ├── unit\u002F\n│   │   ├── transcription.test.ts\n│   │   └── lemur.test.ts\n│   └── integration\u002F\n│       └── assemblyai.test.ts\n└── package.json\n```\n\n## Architecture Layers\n\n```\n┌──────────────────────────────────────────────────────┐\n│                    API Layer                          │\n│  Transcribe endpoint, Webhook handler, Stream token  │\n├──────────────────────────────────────────────────────┤\n│                  Service Layer                        │\n│  TranscriptionService, LeMURService, AudioProcessor  │\n├──────────────────────────────────────────────────────┤\n│                AssemblyAI SDK Layer                   │\n│  client.transcripts, client.streaming, client.lemur  │\n├──────────────────────────────────────────────────────┤\n│               Infrastructure Layer                    │\n│  Database, Redis Cache, Job Queue, Monitoring        │\n└──────────────────────────────────────────────────────┘\n```\n\n## Instructions\n\n### Step 1: Client Layer\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Fclient.ts\nimport { AssemblyAI } from 'assemblyai';\n\nlet client: AssemblyAI | null = null;\n\nexport function getClient(): AssemblyAI {\n  if (!client) {\n    client = new AssemblyAI({\n      apiKey: process.env.ASSEMBLYAI_API_KEY!,\n    });\n  }\n  return client;\n}\n```\n\n### Step 2: Transcription Service\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Ftranscription.ts\nimport { type Transcript, type TranscriptParams } from 'assemblyai';\nimport { getClient } from '.\u002Fclient';\n\nexport interface TranscriptionOptions {\n  speakerLabels?: boolean;\n  sentimentAnalysis?: boolean;\n  entityDetection?: boolean;\n  piiRedaction?: boolean;\n  webhookUrl?: string;\n  model?: 'best' | 'nano';\n}\n\nexport class TranscriptionService {\n  private client = getClient();\n\n  \u002F\u002F Async transcription with webhook (production pattern)\n  async submitForTranscription(\n    audio: string,\n    options: TranscriptionOptions = {}\n  ): Promise\u003C{ transcriptId: string; status: string }> {\n    const params: TranscriptParams = {\n      audio,\n      speech_model: options.model ?? 'best',\n      speaker_labels: options.speakerLabels ?? false,\n      sentiment_analysis: options.sentimentAnalysis ?? false,\n      entity_detection: options.entityDetection ?? false,\n      redact_pii: options.piiRedaction ?? false,\n    };\n\n    if (options.webhookUrl) {\n      params.webhook_url = options.webhookUrl;\n      params.webhook_auth_header_name = 'X-Webhook-Secret';\n      params.webhook_auth_header_value = process.env.ASSEMBLYAI_WEBHOOK_SECRET!;\n    }\n\n    const transcript = options.webhookUrl\n      ? await this.client.transcripts.submit(params)\n      : await this.client.transcripts.transcribe(params);\n\n    return { transcriptId: transcript.id, status: transcript.status };\n  }\n\n  \u002F\u002F Get completed transcript with all data\n  async getTranscript(id: string): Promise\u003CTranscript> {\n    return this.client.transcripts.get(id);\n  }\n\n  \u002F\u002F Delete for GDPR compliance\n  async deleteTranscript(id: string): Promise\u003Cvoid> {\n    await this.client.transcripts.delete(id);\n  }\n\n  \u002F\u002F Batch processing\n  async batchTranscribe(audioUrls: string[], options: TranscriptionOptions = {}) {\n    const PQueue = (await import('p-queue')).default;\n    const queue = new PQueue({ concurrency: 5 });\n\n    return Promise.all(\n      audioUrls.map(url =>\n        queue.add(() => this.submitForTranscription(url, options))\n      )\n    );\n  }\n}\n```\n\n### Step 3: LeMUR Analysis Service\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Flemur.ts\nimport { getClient } from '.\u002Fclient';\n\nexport class LeMURService {\n  private client = getClient();\n\n  async summarize(transcriptIds: string[], context?: string) {\n    const { response } = await this.client.lemur.summary({\n      transcript_ids: transcriptIds,\n      context,\n      answer_format: 'bullet points',\n    });\n    return response;\n  }\n\n  async extractActionItems(transcriptIds: string[]) {\n    const { response } = await this.client.lemur.actionItems({\n      transcript_ids: transcriptIds,\n    });\n    return response;\n  }\n\n  async askQuestions(transcriptIds: string[], questions: string[]) {\n    const { response } = await this.client.lemur.questionAnswer({\n      transcript_ids: transcriptIds,\n      questions: questions.map(q => ({ question: q })),\n    });\n    return response;\n  }\n\n  async customTask(transcriptIds: string[], prompt: string) {\n    const { response } = await this.client.lemur.task({\n      transcript_ids: transcriptIds,\n      prompt,\n    });\n    return response;\n  }\n}\n```\n\n### Step 4: Streaming Service\n\n```typescript\n\u002F\u002F src\u002Fassemblyai\u002Fstreaming.ts\nimport { getClient } from '.\u002Fclient';\n\nexport class StreamingService {\n  private client = getClient();\n\n  async createToken(expiresInSeconds = 300) {\n    return this.client.streaming.createTemporaryToken({\n      expires_in_seconds: expiresInSeconds,\n    });\n  }\n\n  createTranscriber(options: {\n    sampleRate?: number;\n    model?: string;\n    wordBoost?: string[];\n  } = {}) {\n    return this.client.streaming.createService({\n      speech_model: (options.model as any) ?? 'nova-3',\n      sample_rate: options.sampleRate ?? 16000,\n      word_boost: options.wordBoost,\n    });\n  }\n}\n```\n\n### Step 5: Webhook Handler\n\n```typescript\n\u002F\u002F src\u002Fapi\u002Fwebhooks\u002Fassemblyai.ts\nimport { TranscriptionService } from '..\u002F..\u002Fassemblyai\u002Ftranscription';\nimport { LeMURService } from '..\u002F..\u002Fassemblyai\u002Flemur';\n\nconst transcription = new TranscriptionService();\nconst lemur = new LeMURService();\n\nexport async function handleWebhook(req: Request): Promise\u003CResponse> {\n  \u002F\u002F Verify auth\n  const secret = req.headers.get('x-webhook-secret');\n  if (secret !== process.env.ASSEMBLYAI_WEBHOOK_SECRET) {\n    return new Response('Unauthorized', { status: 401 });\n  }\n\n  const { transcript_id, status } = await req.json();\n\n  \u002F\u002F Respond immediately\n  const response = new Response(JSON.stringify({ received: true }), { status: 200 });\n\n  \u002F\u002F Process asynchronously\n  if (status === 'completed') {\n    const transcript = await transcription.getTranscript(transcript_id);\n\n    \u002F\u002F Auto-analyze with LeMUR\n    const summary = await lemur.summarize([transcript_id]);\n    const actionItems = await lemur.extractActionItems([transcript_id]);\n\n    \u002F\u002F Store results in your database\n    await storeResults(transcript, summary, actionItems);\n  }\n\n  return response;\n}\n```\n\n### Step 6: Data Flow\n\n```\nUser uploads audio\n        │\n        ▼\n┌─────────────────┐\n│  API: \u002Ftranscribe│\n│  submit() + URL  │\n└────────┬────────┘\n         │\n         ▼\n┌─────────────────┐\n│  AssemblyAI API  │  ← Processes audio (seconds to minutes)\n│  (async queue)   │\n└────────┬────────┘\n         │ webhook POST\n         ▼\n┌─────────────────┐\n│ Webhook Handler  │\n│ get() transcript │\n└────────┬────────┘\n         │\n    ┌────┴────┐\n    ▼         ▼\n┌────────┐ ┌────────┐\n│ Store  │ │ LeMUR  │  ← Auto-summarize, extract action items\n│ in DB  │ │ Analyze│\n└────────┘ └────────┘\n```\n\n## Configuration\n\n```typescript\n\u002F\u002F src\u002Fconfig.ts\nexport const config = {\n  assemblyai: {\n    model: (process.env.ASSEMBLYAI_MODEL ?? 'best') as 'best' | 'nano',\n    webhookUrl: process.env.ASSEMBLYAI_WEBHOOK_URL,\n    webhookSecret: process.env.ASSEMBLYAI_WEBHOOK_SECRET,\n    defaultFeatures: {\n      speakerLabels: true,\n      sentimentAnalysis: false,\n      entityDetection: false,\n      piiRedaction: process.env.NODE_ENV === 'production',\n    },\n    streaming: {\n      tokenExpiry: 300,\n      sampleRate: 16000,\n      model: 'nova-3',\n    },\n    batch: {\n      concurrency: 5,\n      retries: 3,\n    },\n  },\n};\n```\n\n## Output\n- Layered architecture with clear separation of concerns\n- Transcription service with webhook-based async processing\n- LeMUR analysis pipeline auto-triggered on completion\n- Streaming service with temporary token management\n- Batch processing with concurrency control\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Circular dependencies | Wrong layer boundaries | Services depend on SDK layer, never on API layer |\n| Webhook missed | Processing took too long | Return 200 immediately, process async |\n| LeMUR timeout | Too many transcripts | Batch transcript_ids in groups of 10 |\n| Streaming disconnect | Network interruption | Implement reconnection in StreamingService |\n\n## Resources\n- [AssemblyAI Documentation](https:\u002F\u002Fwww.assemblyai.com\u002Fdocs)\n- [AssemblyAI Node SDK](https:\u002F\u002Fgithub.com\u002FAssemblyAI\u002Fassemblyai-node-sdk)\n- [AssemblyAI API Reference](https:\u002F\u002Fwww.assemblyai.com\u002Fdocs\u002Fapi-reference\u002Foverview)\n- [AssemblyAI Blog — Best Practices](https:\u002F\u002Fwww.assemblyai.com\u002Fblog)\n\n## Next Steps\nFor getting started quickly, see `assemblyai-hello-world`.\n\n",{"name":5,"tags":44,"author":49,"license":15,"version":50,"description":51,"allowed-tools":52,"compatible-with":53},[45,46,47,21,48,22],"saas","ai","speech-to-text","transcription","Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Implement AssemblyAI reference architecture with best-practice project layout.\nUse when designing new AssemblyAI transcription services, reviewing project structure,\nor building production-grade speech-to-text applications.\nTrigger with phrases like \"assemblyai architecture\", \"assemblyai best practices\",\n\"assemblyai project structure\", \"how to organize assemblyai\", \"assemblyai design\".\n","Read, Grep","claude-code","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fassemblyai-reference-architecture",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill assemblyai-reference-architecture",{"tool":69,"command":70},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill assemblyai-reference-architecture",{"tool":72,"command":73},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill assemblyai-reference-architecture","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fassemblyai-reference-architecture\u002FSKILL.md",true,[],[78,92,102,111,117,124],{"skillNo":79,"name":80,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":88,"publishedAt":39},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[83],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[89,90,91],"model","model hierarchy","model hierarchy skill",{"skillNo":93,"name":94,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":98,"publishedAt":39},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[97],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},[99,100,101],"MCP","zoom","zoom mcp",{"skillNo":103,"name":104,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":107,"publishedAt":39},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[108,109,110,100],"sdk","zoom apps sdk","apps",{"skillNo":112,"name":113,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":116,"publishedAt":39},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[113],{"skillNo":118,"name":119,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":123,"publishedAt":39},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[122],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},[119],{"skillNo":125,"name":126,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":129,"publishedAt":39},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[126]]