[{"data":1,"prerenderedAt":3629},["ShallowReactive",2],{"navigation":3,"/blog/software-development/mcp-verified-human-cert-open-source-post":714,"/blog/software-development/mcp-verified-human-cert-open-source-surround":1632,"/blog/software-development/mcp-verified-human-cert-open-source-related":1637},[4,70,203,300,701],{"title":5,"path":6,"stem":7,"children":8,"page":69},"Case Study","/blog/case-study","blog/case-study",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65],{"title":10,"path":11,"stem":12},"Ambistream – Multi-Layer Streaming Platform","/blog/case-study/ambistream-building-a-multi-layer-streaming-platform-from-a-spark-of-an-idea","blog/case-study/ambistream-building-a-multi-layer-streaming-platform-from-a-spark-of-an-idea",{"title":14,"path":15,"stem":16},"Custom Chromecast & AirPlay Casting App","/blog/case-study/chromecast-airplay-casting-app-case-study","blog/case-study/chromecast-airplay-casting-app-case-study",{"title":18,"path":19,"stem":20},"Direct Music Licensing Platform Case Study","/blog/case-study/dlm-music-catalog-case-study","blog/case-study/dlm-music-catalog-case-study",{"title":22,"path":23,"stem":24},"Assembly Instructions App - Galeco Mobile App","/blog/case-study/galeco-mobile-app-case-study","blog/case-study/galeco-mobile-app-case-study",{"title":26,"path":27,"stem":28},"MemoSonic: Building an Audio Memory Game","/blog/case-study/how-we-built-memosonic-accessible-audio-memory-game-flutter","blog/case-study/how-we-built-memosonic-accessible-audio-memory-game-flutter",{"title":30,"path":31,"stem":32},"Loyalty Program App for Shopping Mall","/blog/case-study/loyalty-program-application-case-study","blog/case-study/loyalty-program-application-case-study",{"title":34,"path":35,"stem":36},"Mobile Application for Music Catalogs","/blog/case-study/mobile-app-for-music-catalog","blog/case-study/mobile-app-for-music-catalog",{"title":38,"path":39,"stem":40},"Universal Music Data Parser for 20+ Platforms","/blog/case-study/musicdata-lab-universal-music-data-parser-case-study","blog/case-study/musicdata-lab-universal-music-data-parser-case-study",{"title":42,"path":43,"stem":44},"Panther ML/AI Pricing Recommendation Tool","/blog/case-study/panther-pricing-recommendation-tool-case-study","blog/case-study/panther-pricing-recommendation-tool-case-study",{"title":46,"path":47,"stem":48},"4D Grupa Roofing Wholesalers Platform","/blog/case-study/roofing-wholesalers-website-case-study","blog/case-study/roofing-wholesalers-website-case-study",{"title":50,"path":51,"stem":52},"Talent Alpha HR Frontend Platform","/blog/case-study/talent-alpha-hr-platform-case-study","blog/case-study/talent-alpha-hr-platform-case-study",{"title":54,"path":55,"stem":56},"Ticketing & Events Platform Development","/blog/case-study/ticketing-events-platform-case-study","blog/case-study/ticketing-events-platform-case-study",{"title":58,"path":59,"stem":60},"Turn Fans into Superfans — Roadie.co","/blog/case-study/turn-fans-into-superfans-roadie-co","blog/case-study/turn-fans-into-superfans-roadie-co",{"title":62,"path":63,"stem":64},"Walkative 2.0 Global Booking Engine","/blog/case-study/walkative-2-booking-platform-case-study","blog/case-study/walkative-2-booking-platform-case-study",{"title":66,"path":67,"stem":68},"Why Merch Is the New Royalty Check, and How Tech Is Closing the Gap","/blog/case-study/why-merch-is-the-new-royalty-check","blog/case-study/why-merch-is-the-new-royalty-check",false,{"title":71,"path":72,"stem":73,"children":74,"page":69},"Music Data","/blog/music-data","blog/music-data",[75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167,171,175,179,183,187,191,195,199],{"title":76,"path":77,"stem":78},"13 Distributors, 5 File Formats, Zero Standards -The Reality of Music Royalty Data","/blog/music-data/13-distributors-5-file-formats-zero-standards-the-reality-of-music-royalty-data","blog/music-data/13-distributors-5-file-formats-zero-standards-the-reality-of-music-royalty-data",{"title":80,"path":81,"stem":82},"830 Ways to Say Spotify - Normalizing Music Streaming Data","/blog/music-data/830-ways-to-say-spotify-normalizing-music-streaming-data","blog/music-data/830-ways-to-say-spotify-normalizing-music-streaming-data",{"title":84,"path":85,"stem":86},"Why Music Companies Need AI-Powered Analytics (And How We Built One)","/blog/music-data/ai-powered-analytics-dashboard-django-clickhouse-ollama","blog/music-data/ai-powered-analytics-dashboard-django-clickhouse-ollama",{"title":88,"path":89,"stem":90},"AI Rehearsal: Spaced Repetition for Your Musical Ideas","/blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas","blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas",{"title":92,"path":93,"stem":94},"Audio Project Organization Is a Mess — Here's Why","/blog/music-data/audio-project-organization-mess","blog/music-data/audio-project-organization-mess",{"title":96,"path":97,"stem":98},"Why Audio Search Is Still Broken and How to Fix It with Embeddings","/blog/music-data/audio-search-broken-fix-with-embeddings","blog/music-data/audio-search-broken-fix-with-embeddings",{"title":100,"path":101,"stem":102},"AI Song Structure Analysis: Intro, Verse, Chorus","/blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus","blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus",{"title":104,"path":105,"stem":106},"The Broken Feedback Loop in Music Collaboration","/blog/music-data/broken-feedback-loop-music-collaboration","blog/music-data/broken-feedback-loop-music-collaboration",{"title":108,"path":109,"stem":110},"Building a Claude Skill for DDEX Validation: Automate Music Metadata Checks with AI","/blog/music-data/building-a-claude-skill-for-ddex-validation-music-metadata","blog/music-data/building-a-claude-skill-for-ddex-validation-music-metadata",{"title":112,"path":113,"stem":114},"Building a Custom Music Delivery Platform on the Revelator API","/blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api","blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api",{"title":116,"path":117,"stem":118},"Building a Suno AI Remix App with Nuxt & Firebase","/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase","blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase",{"title":120,"path":121,"stem":122},"C2PA & DDEX: Authenticity Meets Rights in the Age of AI Music","/blog/music-data/c2pa-and-ddex-authenticity-meets-rights-in-the-age-of-ai-music","blog/music-data/c2pa-and-ddex-authenticity-meets-rights-in-the-age-of-ai-music",{"title":124,"path":125,"stem":126},"Data Modeling in MongoDB Using Design Patterns","/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns","blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns",{"title":128,"path":129,"stem":130},"Office Hours with MusicTech Lab's DDEX Expert","/blog/music-data/ddex-office-hours-musictech","blog/music-data/ddex-office-hours-musictech",{"title":132,"path":133,"stem":134},"DDEX Open Source Projects Review","/blog/music-data/ddex-open-source-projects-review","blog/music-data/ddex-open-source-projects-review",{"title":136,"path":137,"stem":138},"Extracting Data from Ableton .als and .asd Files","/blog/music-data/extracting-data-from-ableton-als-asd-files","blog/music-data/extracting-data-from-ableton-als-asd-files",{"title":140,"path":141,"stem":142},"Maciej Dulski on Sound Connections Podcast","/blog/music-data/from-startups-to-musictech-maciej-dulski-on-sound-connections-podcast","blog/music-data/from-startups-to-musictech-maciej-dulski-on-sound-connections-podcast",{"title":144,"path":145,"stem":146},"How to Transcribe Video to Text Using OpenAI Whisper","/blog/music-data/how-to-transcribe-video-to-text-using-whisper","blog/music-data/how-to-transcribe-video-to-text-using-whisper",{"title":148,"path":149,"stem":150},"Epidemic Sound MCP with Claude for Devs","/blog/music-data/how-to-use-epidemic-sound-mcp-with-claude","blog/music-data/how-to-use-epidemic-sound-mcp-with-claude",{"title":152,"path":153,"stem":154},"Hybrid Database Model in Django for Speed","/blog/music-data/hybrid-database-model-in-django-as-a-performance-booster","blog/music-data/hybrid-database-model-in-django-as-a-performance-booster",{"title":156,"path":157,"stem":158},"Introduction to generating DDEX file using Python","/blog/music-data/introduction-to-generating-ddex-file-using-python","blog/music-data/introduction-to-generating-ddex-file-using-python",{"title":160,"path":161,"stem":162},"Maintaining Music Tech Tools: The SLA Dilemma for Small Teams","/blog/music-data/maintaining-music-tech-tools-the-sla-dilemma-for-small-teams","blog/music-data/maintaining-music-tech-tools-the-sla-dilemma-for-small-teams",{"title":164,"path":165,"stem":166},"Querying Bandcamp Revenue Reports with Natural Language — Meet mtl-bandcamp-mcp","/blog/music-data/mtl-bandcamp-mcp-open-source-revenue-dashboard","blog/music-data/mtl-bandcamp-mcp-open-source-revenue-dashboard",{"title":168,"path":169,"stem":170},"mtl-metadata-mcp: Open Source Audio Metadata Embedding for Claude Code","/blog/music-data/mtl-metadata-mcp-open-source-audio-metadata-embedding","blog/music-data/mtl-metadata-mcp-open-source-audio-metadata-embedding",{"title":172,"path":173,"stem":174},"MusicTech Resources for Builders","/blog/music-data/musictech-resources-curated-insights-for-the-musictech-builders","blog/music-data/musictech-resources-curated-insights-for-the-musictech-builders",{"title":176,"path":177,"stem":178},"Poland's Creative Tech and MusicTech Rise","/blog/music-data/polands-creative-tech-sector-is-on-the-rise-and-musictech-is-part-of-it","blog/music-data/polands-creative-tech-sector-is-on-the-rise-and-musictech-is-part-of-it",{"title":180,"path":181,"stem":182},"Batch ISRC Enrichment That Turns Messy Catalogs Into Clean Data","/blog/music-data/scout-isrc-metadata-enrichment-spotify-musicbrainz","blog/music-data/scout-isrc-metadata-enrichment-spotify-musicbrainz",{"title":184,"path":185,"stem":186},"Music Self-Publishing: The Emuze.me Story","/blog/music-data/self-publishing-in-the-music-industry-a-tale-of-emuze-me","blog/music-data/self-publishing-in-the-music-industry-a-tale-of-emuze-me",{"title":188,"path":189,"stem":190},"Understanding the API First Approach","/blog/music-data/understanding-the-api-first-approach","blog/music-data/understanding-the-api-first-approach",{"title":192,"path":193,"stem":194},"The Voice Memo Graveyard Problem","/blog/music-data/voice-memo-graveyard-problem","blog/music-data/voice-memo-graveyard-problem",{"title":196,"path":197,"stem":198},"Which Database for Music Data? Redshift vs BigQuery vs ClickHouse and When to Use Each","/blog/music-data/which-database-for-music-data-redshift-vs-bigquery-vs-clickhouse","blog/music-data/which-database-for-music-data-redshift-vs-bigquery-vs-clickhouse",{"title":200,"path":201,"stem":202},"Why we decided to use wavesurfer.js","/blog/music-data/why-we-decided-to-use-wavesurfer","blog/music-data/why-we-decided-to-use-wavesurfer",{"title":204,"path":205,"stem":206,"children":207,"page":69},"Newsletter","/blog/newsletter","blog/newsletter",[208,212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296],{"title":209,"path":210,"stem":211},"Music Industry Tech Openings (April 2024 Update)","/blog/newsletter/music-industry-tech-openings-april-2024-update","blog/newsletter/music-industry-tech-openings-april-2024-update",{"title":213,"path":214,"stem":215},"Music Industry Tech Openings (April 2025 Update)","/blog/newsletter/music-industry-tech-openings-april-2025-update","blog/newsletter/music-industry-tech-openings-april-2025-update",{"title":217,"path":218,"stem":219},"Music Industry Tech Openings (August 2024 Update)","/blog/newsletter/music-industry-tech-openings-august-2024-update","blog/newsletter/music-industry-tech-openings-august-2024-update",{"title":221,"path":222,"stem":223},"Music Industry Tech Openings (December 2024 Update)","/blog/newsletter/music-industry-tech-openings-december-2024-update","blog/newsletter/music-industry-tech-openings-december-2024-update",{"title":225,"path":226,"stem":227},"Music Industry Tech Openings (February 2025 Update)","/blog/newsletter/music-industry-tech-openings-february-2025-update","blog/newsletter/music-industry-tech-openings-february-2025-update",{"title":229,"path":230,"stem":231},"Music Industry Tech Openings (January 2025 Update)","/blog/newsletter/music-industry-tech-openings-january-2025-update","blog/newsletter/music-industry-tech-openings-january-2025-update",{"title":233,"path":234,"stem":235},"Music Industry Tech Openings (July 2024 Update)","/blog/newsletter/music-industry-tech-openings-july-2024-update","blog/newsletter/music-industry-tech-openings-july-2024-update",{"title":237,"path":238,"stem":239},"Music Industry Tech Openings (June 2024 Update)","/blog/newsletter/music-industry-tech-openings-june-2024-update","blog/newsletter/music-industry-tech-openings-june-2024-update",{"title":241,"path":242,"stem":243},"Music Industry Tech Openings (March 2025 Update)","/blog/newsletter/music-industry-tech-openings-march-2025-update","blog/newsletter/music-industry-tech-openings-march-2025-update",{"title":245,"path":246,"stem":247},"Music Industry Tech Openings (May 2024 Update)","/blog/newsletter/music-industry-tech-openings-may-2024-update","blog/newsletter/music-industry-tech-openings-may-2024-update",{"title":249,"path":250,"stem":251},"Music Industry Tech Openings (May 2025 Update)","/blog/newsletter/music-industry-tech-openings-may-2025","blog/newsletter/music-industry-tech-openings-may-2025",{"title":253,"path":254,"stem":255},"Music Industry Tech Openings (November 2024 Update)","/blog/newsletter/music-industry-tech-openings-november-2024-update","blog/newsletter/music-industry-tech-openings-november-2024-update",{"title":257,"path":258,"stem":259},"Music Industry Tech Openings (October 2024 Update)","/blog/newsletter/music-industry-tech-openings-october-2024-update","blog/newsletter/music-industry-tech-openings-october-2024-update",{"title":261,"path":262,"stem":263},"Music Industry Tech Openings (September 2024 Update)","/blog/newsletter/music-industry-tech-openings-september-2024-update","blog/newsletter/music-industry-tech-openings-september-2024-update",{"title":265,"path":266,"stem":267},"MusicTech Insights #1 by Maciej Dulski","/blog/newsletter/musictech-insights-1-curated-by-maciej-dulski","blog/newsletter/musictech-insights-1-curated-by-maciej-dulski",{"title":269,"path":270,"stem":271},"Feeling the MusicTech Momentum","/blog/newsletter/musictech-insights-2-curated-by-maciej-dulski","blog/newsletter/musictech-insights-2-curated-by-maciej-dulski",{"title":273,"path":274,"stem":275},"AI in Music: Hype, Hope, and a Human Touch","/blog/newsletter/musictech-insights-3-curated-by-drew-thurlow","blog/newsletter/musictech-insights-3-curated-by-drew-thurlow",{"title":277,"path":278,"stem":279},"The Music Metadata Conundrum","/blog/newsletter/musictech-insights-4-curated-by-amanda-schupf","blog/newsletter/musictech-insights-4-curated-by-amanda-schupf",{"title":281,"path":282,"stem":283},"7 Rounds in the First 10 Days of November 2025","/blog/newsletter/musictech-insights-5-curated-by-maciej-dulski","blog/newsletter/musictech-insights-5-curated-by-maciej-dulski",{"title":285,"path":286,"stem":287},"The End of an Era: It's All About to Crash","/blog/newsletter/musictech-insights-6-curated-by-sigurdur-arnason","blog/newsletter/musictech-insights-6-curated-by-sigurdur-arnason",{"title":289,"path":290,"stem":291},"Low-Code Magic Won't Solve MusicTech Reality","/blog/newsletter/musictech-insights-7-curated-by-mariusz-smenzyk","blog/newsletter/musictech-insights-7-curated-by-mariusz-smenzyk",{"title":293,"path":294,"stem":295},"The New Economics of Game Music","/blog/newsletter/musictech-insights-8-curated-by-kenny-vaughan","blog/newsletter/musictech-insights-8-curated-by-kenny-vaughan",{"title":297,"path":298,"stem":299},"Music Business Meets Direct-to-Fan","/blog/newsletter/musictech-insights-9-curated-by-yaw-asamani","blog/newsletter/musictech-insights-9-curated-by-yaw-asamani",{"title":301,"path":302,"stem":303,"children":304,"page":69},"Software Development","/blog/software-development","blog/software-development",[305,309,313,317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401,405,409,413,417,421,425,429,433,437,441,445,449,453,457,461,465,469,473,477,481,485,489,493,497,501,505,509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585,589,593,597,601,605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665,669,673,677,681,685,689,693,697],{"title":306,"path":307,"stem":308},"Benefits of Outsourcing Software Development","/blog/software-development/10-benefits-of-outsourcing-software-development-services","blog/software-development/10-benefits-of-outsourcing-software-development-services",{"title":310,"path":311,"stem":312},"10 Steps to Find the Best MVP Developers","/blog/software-development/10-steps-to-find-the-best-mvp-developers-for-your-startup-idea","blog/software-development/10-steps-to-find-the-best-mvp-developers-for-your-startup-idea",{"title":314,"path":315,"stem":316},"1,200 Looms Later: How Async Video Became My Development Superpower","/blog/software-development/1200-looms-how-async-video-became-our-development-superpower","blog/software-development/1200-looms-how-async-video-became-our-development-superpower",{"title":318,"path":319,"stem":320},"Communication Strategy in Outsourcing Projects","/blog/software-development/5-steps-to-implement-an-effective-communication-strategy-in-outsourcing-software-development-project","blog/software-development/5-steps-to-implement-an-effective-communication-strategy-in-outsourcing-software-development-project",{"title":322,"path":323,"stem":324},"7 Best Practices for Outsourcing Software Development","/blog/software-development/7-best-practices-for-outsourcing-software-development","blog/software-development/7-best-practices-for-outsourcing-software-development",{"title":326,"path":327,"stem":328},"9 Reasons Why Saleor.io Is Best for eCommerce","/blog/software-development/9-reasons-why-the-saleor-io-platform-is-the-best-choice-for-your-ecommerce-website","blog/software-development/9-reasons-why-the-saleor-io-platform-is-the-best-choice-for-your-ecommerce-website",{"title":330,"path":331,"stem":332},"A Look at Bravelab.io’s Clutch 2021 Year In Review","/blog/software-development/a-look-at-bravelab-ios-clutch-2021-year-in-review","blog/software-development/a-look-at-bravelab-ios-clutch-2021-year-in-review",{"title":334,"path":335,"stem":336},"A quick introduction to profit sharing implementation","/blog/software-development/a-quick-introduction-to-profit-sharing-implementation","blog/software-development/a-quick-introduction-to-profit-sharing-implementation",{"title":338,"path":339,"stem":340},"AI Audio Similarity Search: The Future of Sound Library Discovery","/blog/software-development/ai-audio-similarity-search-for-sound-libraries","blog/software-development/ai-audio-similarity-search-for-sound-libraries",{"title":342,"path":343,"stem":344},"Automate Repetitive Tasks for Better Results","/blog/software-development/automate-repetitive-tasks-to-improve-your-business-performance","blog/software-development/automate-repetitive-tasks-to-improve-your-business-performance",{"title":346,"path":347,"stem":348},"Automating Success: The Art of Unified Documentation","/blog/software-development/automating-success-the-art-of-unified-documentation","blog/software-development/automating-success-the-art-of-unified-documentation",{"title":350,"path":351,"stem":352},"Brave 3.0 Website Redesign, Part 2: Solution","/blog/software-development/brave-3-0-how-we-conducted-website-redesign-part-2-solution","blog/software-development/brave-3-0-how-we-conducted-website-redesign-part-2-solution",{"title":354,"path":355,"stem":356},"Brave 3.0, Part 4: Tech Stack and Recap","/blog/software-development/brave-3-0-part-4-technologies-behind-and-final-series-recap","blog/software-development/brave-3-0-part-4-technologies-behind-and-final-series-recap",{"title":358,"path":359,"stem":360},"Brave 3.0 – redesign process part 1. The Challenge","/blog/software-development/brave-3-0-redesign-process-part-1-challenge","blog/software-development/brave-3-0-redesign-process-part-1-challenge",{"title":362,"path":363,"stem":364},"Brave 3.0 – redesign process, part 3. Lesson learned","/blog/software-development/brave-3-0-redesign-process-part-3-lesson-learned","blog/software-development/brave-3-0-redesign-process-part-3-lesson-learned",{"title":366,"path":367,"stem":368},"Bravelab.io: Top Software Developer by Clutch","/blog/software-development/bravelab-io-is-recognized-as-a-top-custom-software-developer-by-clutch","blog/software-development/bravelab-io-is-recognized-as-a-top-custom-software-developer-by-clutch",{"title":370,"path":371,"stem":372},"Bravelab.io: Top Developer in Poland by Clutch","/blog/software-development/bravelab-io-named-top-software-developer-in-poland-by-clutch","blog/software-development/bravelab-io-named-top-software-developer-in-poland-by-clutch",{"title":374,"path":375,"stem":376},"MusicTech Lab Partners with LALAL.AI","/blog/software-development/bravelab-partners-with-the-audio-lalal-ai","blog/software-development/bravelab-partners-with-the-audio-lalal-ai",{"title":378,"path":379,"stem":380},"MusicTech Lab Partners with The Audio Programmer","/blog/software-development/bravelab-partners-with-the-audio-programmer","blog/software-development/bravelab-partners-with-the-audio-programmer",{"title":382,"path":383,"stem":384},"Bravelab's team about productivity","/blog/software-development/bravelabs-team-about-productivity","blog/software-development/bravelabs-team-about-productivity",{"title":386,"path":387,"stem":388},"Braveloper","/blog/software-development/braveloper","blog/software-development/braveloper",{"title":390,"path":391,"stem":392},"Bravely App: Boost Productivity with Django","/blog/software-development/bravely-app-how-to-be-more-productive-with-django-quick","blog/software-development/bravely-app-how-to-be-more-productive-with-django-quick",{"title":394,"path":395,"stem":396},"DIY MIDI Controller for Ableton with Arduino","/blog/software-development/building-a-diy-midi-controller-for-ableton-live-with-arduino","blog/software-development/building-a-diy-midi-controller-for-ableton-live-with-arduino",{"title":398,"path":399,"stem":400},"Change Detection mechanism in Angular","/blog/software-development/change-detection-mechanism-in-angular","blog/software-development/change-detection-mechanism-in-angular",{"title":402,"path":403,"stem":404},"Communication Channels in Remote Work","/blog/software-development/comparison-of-the-communication-channels-in-remote-work","blog/software-development/comparison-of-the-communication-channels-in-remote-work",{"title":406,"path":407,"stem":408},"Connecting Your Max for Live Device to a Cloud API","/blog/software-development/connecting-your-max-for-live-device-to-a-cloud-api","blog/software-development/connecting-your-max-for-live-device-to-a-cloud-api",{"title":410,"path":411,"stem":412},"From Voice Memo to Studio: The Cross-Platform Problem for Creators","/blog/software-development/cross-platform-problem-for-creators","blog/software-development/cross-platform-problem-for-creators",{"title":414,"path":415,"stem":416},"Cultural transformation through the pandemic era","/blog/software-development/cultural-transformation-through-the-pandemic-era","blog/software-development/cultural-transformation-through-the-pandemic-era",{"title":418,"path":419,"stem":420},"D-Commerce Decoded: Cutting Through the Hype","/blog/software-development/d-commerce-decoded-cutting-through-the-hype","blog/software-development/d-commerce-decoded-cutting-through-the-hype",{"title":422,"path":423,"stem":424},"Dev Meeting 002: Intro to DDD","/blog/software-development/dev-meeting-002-introduction-to-domain-driven-design-ddd","blog/software-development/dev-meeting-002-introduction-to-domain-driven-design-ddd",{"title":426,"path":427,"stem":428},"Dev Meeting 003: Web3 Primer","/blog/software-development/dev-meeting-003-web3-primer","blog/software-development/dev-meeting-003-web3-primer",{"title":430,"path":431,"stem":432},"Dev Meeting 004: Introduction to Event Storming","/blog/software-development/dev-meeting-004-introduction-to-event-storming","blog/software-development/dev-meeting-004-introduction-to-event-storming",{"title":434,"path":435,"stem":436},"Dev Meeting 001: Kubernetes is a Framework","/blog/software-development/dev-meeting-kubernetes-is-a-framework","blog/software-development/dev-meeting-kubernetes-is-a-framework",{"title":438,"path":439,"stem":440},"Did You Know? 10 Developer Tips from Real Codebases","/blog/software-development/did-you-know-dev-tips-part-1","blog/software-development/did-you-know-dev-tips-part-1",{"title":442,"path":443,"stem":444},"10 Surprising MusicTech Facts (Part 2)","/blog/software-development/did-you-know-musictech-facts-part-2","blog/software-development/did-you-know-musictech-facts-part-2",{"title":446,"path":447,"stem":448},"Django-cms and GraphQL","/blog/software-development/django-cms-and-graphql","blog/software-development/django-cms-and-graphql",{"title":450,"path":451,"stem":452},"Does Zappa make it super easy?","/blog/software-development/does-zappa-make-it-super-easy","blog/software-development/does-zappa-make-it-super-easy",{"title":454,"path":455,"stem":456},"Establishing cooperation between Netlify and Bravelab","/blog/software-development/establishing-cooperation-between-netlify-and-bravelab","blog/software-development/establishing-cooperation-between-netlify-and-bravelab",{"title":458,"path":459,"stem":460},"Export Ableton Locators to JSON via Max for Live","/blog/software-development/exporting-ableton-live-locators-to-json-with-max-for-live","blog/software-development/exporting-ableton-live-locators-to-json-with-max-for-live",{"title":462,"path":463,"stem":464},"IT Outsourcing: Success and Failure Factors","/blog/software-development/factors-that-contribute-to-the-success-or-failure-of-an-it-outsourcing-project","blog/software-development/factors-that-contribute-to-the-success-or-failure-of-an-it-outsourcing-project",{"title":466,"path":467,"stem":468},"Flutter 2022 Strategy: Analyzing the Roadmap","/blog/software-development/flutter-strategy-for-2022-analyzing-the-new-flutter-roadmap","blog/software-development/flutter-strategy-for-2022-analyzing-the-new-flutter-roadmap",{"title":470,"path":471,"stem":472},"Git Better #1 — Commit Message Convention","/blog/software-development/git-better-1-see-more-with-a-commit-message-convention","blog/software-development/git-better-1-see-more-with-a-commit-message-convention",{"title":474,"path":475,"stem":476},"Hasura in action. How to use it with Django","/blog/software-development/hasura-in-action","blog/software-development/hasura-in-action",{"title":478,"path":479,"stem":480},"Holacracy why and where we are","/blog/software-development/holacracy-why-and-where-we-are","blog/software-development/holacracy-why-and-where-we-are",{"title":482,"path":483,"stem":484},"How does JavaScript work","/blog/software-development/how-does-javascript-work","blog/software-development/how-does-javascript-work",{"title":486,"path":487,"stem":488},"How important is good UX/UI design?","/blog/software-development/how-important-is-good-ux-ui-design","blog/software-development/how-important-is-good-ux-ui-design",{"title":490,"path":491,"stem":492},"How repetitive tasks impact your business","/blog/software-development/how-repetitive-tasks-impact-your-business","blog/software-development/how-repetitive-tasks-impact-your-business",{"title":494,"path":495,"stem":496},"Becoming a Vue.js Dev: Do Paid Trials Work?","/blog/software-development/how-to-become-a-vue-js-developer-and-whether-paid-trials-in-it-work-out","blog/software-development/how-to-become-a-vue-js-developer-and-whether-paid-trials-in-it-work-out",{"title":498,"path":499,"stem":500},"How to Build an MVP in 6 Steps","/blog/software-development/how-to-build-a-minimum-viable-product-mvp-in-6-steps","blog/software-development/how-to-build-a-minimum-viable-product-mvp-in-6-steps",{"title":502,"path":503,"stem":504},"How to conduct workshops for creative industry?","/blog/software-development/how-to-conduct-workshops-for-creative-industry","blog/software-development/how-to-conduct-workshops-for-creative-industry",{"title":506,"path":507,"stem":508},"How to easily create form in Angular","/blog/software-development/how-to-easily-create-form-in-angular","blog/software-development/how-to-easily-create-form-in-angular",{"title":510,"path":511,"stem":512},"How to export orders in Saleor.io to XLSX file","/blog/software-development/how-to-export-orders-in-saleor-io-to-xlsx-file","blog/software-development/how-to-export-orders-in-saleor-io-to-xlsx-file",{"title":514,"path":515,"stem":516},"Handling High Loads on E-Commerce Platforms","/blog/software-development/how-to-handle-high-loads-on-e-commerce-platform-with-ease","blog/software-development/how-to-handle-high-loads-on-e-commerce-platform-with-ease",{"title":518,"path":519,"stem":520},"How to launch Saleor.io shop instance within 40h","/blog/software-development/how-to-launch-saleor-io-shop-instance-within-40h","blog/software-development/how-to-launch-saleor-io-shop-instance-within-40h",{"title":522,"path":523,"stem":524},"First Steps to Build a Business Relationship","/blog/software-development/how-to-make-the-first-step-to-establish-a-business-relationship","blog/software-development/how-to-make-the-first-step-to-establish-a-business-relationship",{"title":526,"path":527,"stem":528},"Multi-Tenant Apps with Django and Saleor.io","/blog/software-development/how-to-manage-tenants-in-the-multitenant-app-based-on-django-tenants-and-saleor-io-platform","blog/software-development/how-to-manage-tenants-in-the-multitenant-app-based-on-django-tenants-and-saleor-io-platform",{"title":530,"path":531,"stem":532},"Notion Backup Tool Built in 3 Days with Python","/blog/software-development/how-we-built-a-notion-backup-tool-in-3-days-with-pythonvue-and-why","blog/software-development/how-we-built-a-notion-backup-tool-in-3-days-with-pythonvue-and-why",{"title":534,"path":535,"stem":536},"Important new features in Python 3.8","/blog/software-development/important-new-features-in-python-3-8","blog/software-development/important-new-features-in-python-3-8",{"title":538,"path":539,"stem":540},"Installing Proxmox on dedicated server from OVH","/blog/software-development/installing-proxmox-on-dedicated-server-from-ovh","blog/software-development/installing-proxmox-on-dedicated-server-from-ovh",{"title":542,"path":543,"stem":544},"Integrating SignNow E-Signatures into Your Django Application","/blog/software-development/integrating-signnow-e-signatures-into-your-django-application","blog/software-development/integrating-signnow-e-signatures-into-your-django-application",{"title":546,"path":547,"stem":548},"Tempus Metronome and GetSongBPM API","/blog/software-development/integrating-tempus-metronome-with-the-getsongbpm-api-what-bpm-really-means-and-how-to-use-it","blog/software-development/integrating-tempus-metronome-with-the-getsongbpm-api-what-bpm-really-means-and-how-to-use-it",{"title":550,"path":551,"stem":552},"Introducing MusicTech Poland","/blog/software-development/introducing-musictech-poland","blog/software-development/introducing-musictech-poland",{"title":554,"path":555,"stem":556},"Vue.js as a Frontend for Saleor.io","/blog/software-development/is-it-possible-to-use-vue-js-as-a-frontend-for-saleor-io-platform","blog/software-development/is-it-possible-to-use-vue-js-as-a-frontend-for-saleor-io-platform",{"title":558,"path":559,"stem":560},"Is your business ready for the cashless era?","/blog/software-development/is-your-business-ready-for-the-cashless-era","blog/software-development/is-your-business-ready-for-the-cashless-era",{"title":562,"path":563,"stem":564},"Is your face ready to buy?","/blog/software-development/is-your-face-ready-to-buy","blog/software-development/is-your-face-ready-to-buy",{"title":566,"path":567,"stem":568},"JS Frameworks: Trends and Opportunities","/blog/software-development/javascript-trending-frameworks-and-market-opportunities","blog/software-development/javascript-trending-frameworks-and-market-opportunities",{"title":570,"path":571,"stem":572},"Kanban Board: Boost Your Team Productivity","/blog/software-development/kanban-board-methodology-hack-your-companys-productivity","blog/software-development/kanban-board-methodology-hack-your-companys-productivity",{"title":574,"path":575,"stem":576},"Verified Human Cert MCP Server: Prove Your Music Is Human-Made, Right from the Terminal","/blog/software-development/mcp-verified-human-cert-open-source","blog/software-development/mcp-verified-human-cert-open-source",{"title":578,"path":579,"stem":580},"Migrating from TravisCI to Github Actions","/blog/software-development/migrating-from-travisci-to-github-actions","blog/software-development/migrating-from-travisci-to-github-actions",{"title":582,"path":583,"stem":584},"MusicTech Lab: Top Software Developer by Clutch","/blog/software-development/musictechlab-is-recognized-as-a-top-custom-software-developer-by-clutch","blog/software-development/musictechlab-is-recognized-as-a-top-custom-software-developer-by-clutch",{"title":586,"path":587,"stem":588},"MusicXML: Standard for Music Notation","/blog/software-development/musicxml-standard-for-music-notation-and-education","blog/software-development/musicxml-standard-for-music-notation-and-education",{"title":590,"path":591,"stem":592},"Only a few books but dozens of ideas","/blog/software-development/only-a-few-books-but-dozens-of-ideas","blog/software-development/only-a-few-books-but-dozens-of-ideas",{"title":594,"path":595,"stem":596},"Overdue Invoices and Issue Tracker Integration","/blog/software-development/overdue-invoices-integration-with-the-issue-tracking-system","blog/software-development/overdue-invoices-integration-with-the-issue-tracking-system",{"title":598,"path":599,"stem":600},"Performing SAML SSO using JWT in Django","/blog/software-development/performing-saml-sso-using-jwt-in-django","blog/software-development/performing-saml-sso-using-jwt-in-django",{"title":602,"path":603,"stem":604},"Progressive Web Apps for Mobile Development","/blog/software-development/progressive-web-apps-a-new-way-of-creating-mobile-application","blog/software-development/progressive-web-apps-a-new-way-of-creating-mobile-application",{"title":606,"path":607,"stem":608},"Recruitment System: Gmail, Jira, and CRM","/blog/software-development/recruitment-system-integrating-gmail-bravely-jira-slack-and-copper-crm","blog/software-development/recruitment-system-integrating-gmail-bravely-jira-slack-and-copper-crm",{"title":610,"path":611,"stem":612},"Scratch Me: Chrome Extension for Leads","/blog/software-development/scratch-me-a-simple-chrome-extension-which-will-increase-your-productivity","blog/software-development/scratch-me-a-simple-chrome-extension-which-will-increase-your-productivity",{"title":614,"path":615,"stem":616},"Scratch Me – integration with the Copper CRM","/blog/software-development/scratch-me-integration-with-the-copper-crm","blog/software-development/scratch-me-integration-with-the-copper-crm",{"title":618,"path":619,"stem":620},"SignNow MCP Server: E-Signatures Straight from Claude Code","/blog/software-development/signnow-mcp-server-e-signatures-from-claude-code","blog/software-development/signnow-mcp-server-e-signatures-from-claude-code",{"title":622,"path":623,"stem":624},"Music Industry Tech Openings (March 2024 Update)","/blog/software-development/technical-job-opportunities-in-the-music-industry","blog/software-development/technical-job-opportunities-in-the-music-industry",{"title":626,"path":627,"stem":628},"Thanks app – a Management 3.0 solution","/blog/software-development/thanks-app-a-management-3-0-solution","blog/software-development/thanks-app-a-management-3-0-solution",{"title":630,"path":631,"stem":632},"Colonial Pipeline Case: 7 Security Reminders","/blog/software-development/the-case-of-colonial-pipeline-and-7-security-reminders","blog/software-development/the-case-of-colonial-pipeline-and-7-security-reminders",{"title":634,"path":635,"stem":636},"The Evolution and Future of E-commerce Platforms","/blog/software-development/the-evolution-and-future-of-e-commerce-platforms","blog/software-development/the-evolution-and-future-of-e-commerce-platforms",{"title":638,"path":639,"stem":640},"The Gender Gap in the Tech Industry","/blog/software-development/the-gender-gap-in-the-tech-industry","blog/software-development/the-gender-gap-in-the-tech-industry",{"title":642,"path":643,"stem":644},"First Attempt to Implement 4DX at Bravelab.io","/blog/software-development/the-very-first-attempt-to-implement-4dx-in-bravelab-io","blog/software-development/the-very-first-attempt-to-implement-4dx-in-bravelab-io",{"title":646,"path":647,"stem":648},"The WTF Scale: IT Project Complexity","/blog/software-development/the-wtf-programming-scale-measuring-it-project-complexity","blog/software-development/the-wtf-programming-scale-measuring-it-project-complexity",{"title":650,"path":651,"stem":652},"Top 10 articles through the eyes of our developers","/blog/software-development/top-10-articles-through-the-eyes-of-our-developers","blog/software-development/top-10-articles-through-the-eyes-of-our-developers",{"title":654,"path":655,"stem":656},"Top 6 apps made with Flutter","/blog/software-development/top-6-apps-made-with-flutter","blog/software-development/top-6-apps-made-with-flutter",{"title":658,"path":659,"stem":660},"Uber 101: How Uber Made It to the Top","/blog/software-development/uber-101-how-this-ride-sharing-behemoth-made-it-to-the-top","blog/software-development/uber-101-how-this-ride-sharing-behemoth-made-it-to-the-top",{"title":662,"path":663,"stem":664},"MusicTech Lab Partners with Music Glue","/blog/software-development/unifying-artists-and-audiences-exploring-music-glue","blog/software-development/unifying-artists-and-audiences-exploring-music-glue",{"title":666,"path":667,"stem":668},"Why AI Will Defeat Traditional HR","/blog/software-development/warning-why-artificial-intelligence-will-defeat-traditional-hr","blog/software-development/warning-why-artificial-intelligence-will-defeat-traditional-hr",{"title":670,"path":671,"stem":672},"What is a Discovery Document?","/blog/software-development/what-is-discovery-document","blog/software-development/what-is-discovery-document",{"title":674,"path":675,"stem":676},"What is Flutter, and Why is it Worth Considering?","/blog/software-development/what-is-flutter-and-why-is-it-worth-considering","blog/software-development/what-is-flutter-and-why-is-it-worth-considering",{"title":678,"path":679,"stem":680},"What is a Watermarked Song?","/blog/software-development/what-is-watermarked-song","blog/software-development/what-is-watermarked-song",{"title":682,"path":683,"stem":684},"Choosing a Frontend Framework for the Web","/blog/software-development/which-framework-should-you-choose-for-the-frontend-web-platform-development","blog/software-development/which-framework-should-you-choose-for-the-frontend-web-platform-development",{"title":686,"path":687,"stem":688},"Why DAWs Are the Wrong Tool for Starting a Song","/blog/software-development/why-daws-wrong-tool-for-starting-song","blog/software-development/why-daws-wrong-tool-for-starting-song",{"title":690,"path":691,"stem":692},"Why the Programming World Loves Python","/blog/software-development/why-the-programming-world-loves-python","blog/software-development/why-the-programming-world-loves-python",{"title":694,"path":695,"stem":696},"Why We Don't Build Chat From Scratch (And Neither Should You)","/blog/software-development/why-we-dont-build-chat-from-scratch","blog/software-development/why-we-dont-build-chat-from-scratch",{"title":698,"path":699,"stem":700},"Why we use Sanity.io","/blog/software-development/why-we-use-sanity-io","blog/software-development/why-we-use-sanity-io",{"title":702,"path":703,"stem":704,"children":705,"page":69},"Sportstech","/blog/sportstech","blog/sportstech",[706,710],{"title":707,"path":708,"stem":709},"BeatBuddy Replay: Video Analysis App Challenges","/blog/sportstech/beatbuddy-replay-video-analysis-app-for-swimmers-flutter","blog/sportstech/beatbuddy-replay-video-analysis-app-for-swimmers-flutter",{"title":711,"path":712,"stem":713},"How to Create a Watch Face App for Garmin Watch","/blog/sportstech/how-to-create-watch-face-app-for-garmin-watch","blog/sportstech/how-to-create-watch-face-app-for-garmin-watch",{"id":715,"title":574,"authors":716,"badge":722,"body":725,"category":1586,"client":1587,"date":1588,"description":1589,"extension":1590,"faq":1591,"featured":1607,"featuredOrder":1608,"hidden":69,"image":1609,"keyTakeaways":1612,"meta":1622,"navigation":1607,"path":575,"seo":1623,"status":1587,"stem":576,"tags":1626,"teaser":1587,"__hash__":1631},"posts/blog/software-development/mcp-verified-human-cert-open-source.md",[717],{"name":718,"to":719,"avatar":720},"Mariusz Smenżyk","https://www.linkedin.com/in/mariusz-smenzyk/",{"src":721},"/images/people/mariusz-smenzyk2.webp",{"label":723,"color":724},"Open Source","#7c3aed",{"type":726,"value":727,"toc":1572},"minimark",[728,740,749,754,757,838,841,844,864,868,877,899,909,912,919,925,929,932,957,961,966,1008,1012,1023,1181,1185,1188,1205,1212,1218,1222,1231,1255,1381,1457,1460,1466,1470,1483,1513,1517,1525,1528,1532,1568],[729,730,731,732,739],"p",{},"The line between human-made and machine-generated music is getting blurry. ",[733,734,738],"a",{"href":735,"rel":736},"https://verifiedhumancert.com",[737],"nofollow","Verified Human Cert"," (VHC) is one answer to that problem: a public registry where artists and labels certify that their tracks were created by humans.",[729,741,742,743,748],{},"We wanted to query that registry without leaving our terminal. So we built an MCP server for it and open-sourced it: ",[733,744,747],{"href":745,"rel":746},"https://github.com/musictechlab/mcp-verifiedhumancert",[737],"mcp-verifiedhumancert",".",[750,751,753],"h2",{"id":752},"what-the-server-does","What the server does",[729,755,756],{},"The server exposes six tools that map directly to the VHC public API:",[758,759,760,773],"table",{},[761,762,763],"thead",{},[764,765,766,770],"tr",{},[767,768,769],"th",{},"Tool",[767,771,772],{},"What it does",[774,775,776,788,798,808,818,828],"tbody",{},[764,777,778,785],{},[779,780,781],"td",{},[782,783,784],"code",{},"vhc_verify_isrc",[779,786,787],{},"Verify a certification by ISRC code",[764,789,790,795],{},[779,791,792],{},[782,793,794],{},"vhc_verify_track",[779,796,797],{},"Check certification status by artist + track name",[764,799,800,805],{},[779,801,802],{},[782,803,804],{},"vhc_verify_cert",[779,806,807],{},"Look up a certification by cert number",[764,809,810,815],{},[779,811,812],{},[782,813,814],{},"vhc_registry",[779,816,817],{},"List recently issued certifications",[764,819,820,825],{},[779,821,822],{},[782,823,824],{},"vhc_stats",[779,826,827],{},"Platform statistics, tier breakdowns, totals",[764,829,830,835],{},[779,831,832],{},[782,833,834],{},"vhc_pricing",[779,836,837],{},"Current pricing and bundle options",[729,839,840],{},"No API key needed. The VHC registry is public, and all these endpoints are read-only.",[729,842,843],{},"Once connected, you just talk to Claude:",[845,846,847,854,859],"ul",{},[848,849,850],"li",{},[851,852,853],"em",{},"\"Is ISRC USHM82148308 certified as human-made?\"",[848,855,856],{},[851,857,858],{},"\"Check if 'Yesterday' by The Beatles has a VHC certification\"",[848,860,861],{},[851,862,863],{},"\"Show me the latest certified tracks\"",[750,865,867],{"id":866},"the-multi-agent-workflow","The multi-agent workflow",[729,869,870,871,876],{},"This is where it gets interesting. We already had ",[733,872,875],{"href":873,"rel":874},"https://github.com/musictechlab/mcp-metadata",[737],"mcp-metadata",", our open-source MCP server for reading and writing audio file metadata (ID3 tags, ISRC codes, Vorbis comments). Combining the two servers creates a multi-agent pipeline:",[878,879,880,887,893],"ol",{},[848,881,882,886],{},[883,884,885],"strong",{},"Agent 1"," (mcp-metadata) reads the ISRC code from an audio file",[848,888,889,892],{},[883,890,891],{},"Agent 2"," (mcp-verifiedhumancert) verifies that ISRC against the VHC registry",[848,894,895,898],{},[883,896,897],{},"Claude"," orchestrates both agents and presents the result",[900,901,907],"pre",{"className":902,"code":904,"language":905,"meta":906},[903],"language-text","User: \"Read the ISRC from song.flac and check if it's certified\"\n\nAgent 1 (mcp-metadata): metadata_read(\"song.flac\") -> ISRC: USHM82148308\nAgent 2 (mcp-verifiedhumancert): vhc_verify_isrc(\"USHM82148308\") -> certified: true\n","text","",[782,908,904],{"__ignoreMap":906},[729,910,911],{},"Two MCP servers, two specialized agents, one orchestrator. No glue code, no custom integrations. Claude handles the coordination.",[729,913,914],{},[915,916],"img",{"alt":917,"src":918},"Multi-agent workflow: mcp-metadata reads ISRC from an audio file, then mcp-verifiedhumancert checks the VHC registry","/images/blog/musictechlab_blog_vhc_mcp_multi_agent.webp",[920,921,922],"tip",{},[729,923,924],{},"This pattern scales to any number of MCP servers. Each server handles one domain, and Claude routes between them based on what the user asks.",[750,926,928],{"id":927},"why-this-matters-for-the-music-industry","Why this matters for the music industry",[729,930,931],{},"The rise of generative audio tools has created a trust problem. Listeners, labels, and platforms need a way to distinguish human-created work from machine-generated output. VHC provides that layer of trust, and our MCP server makes it accessible to developers and tooling without building custom API integrations.",[933,934,941,947,952],"div",{"className":935},[936,937,938,939,940],"grid","grid-cols-1","md:grid-cols-3","gap-4","my-8",[942,943],"spotlight-card",{"description":944,"icon":945,"title":946},"Check any track's certification status by ISRC, artist name, or cert number.","i-lucide-shield-check","Verify Tracks",[942,948],{"description":949,"icon":950,"title":951},"Combine with mcp-metadata to read ISRC from files and verify automatically.","i-lucide-link","Multi-Agent Pipeline",[942,953],{"description":954,"icon":955,"title":956},"The VHC registry is public. No API keys, no signup, no rate limits to worry about.","i-lucide-globe","No Auth Required",[750,958,960],{"id":959},"setting-it-up","Setting it up",[962,963,965],"h3",{"id":964},"_1-clone-and-install","1. Clone and install",[900,967,971],{"className":968,"code":969,"language":970,"meta":906,"style":906},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","git clone https://github.com/musictechlab/mcp-verifiedhumancert.git\ncd mcp-verifiedhumancert\npoetry install\n","bash",[782,972,973,989,999],{"__ignoreMap":906},[974,975,978,982,986],"span",{"class":976,"line":977},"line",1,[974,979,981],{"class":980},"sBMFI","git",[974,983,985],{"class":984},"sfazB"," clone",[974,987,988],{"class":984}," https://github.com/musictechlab/mcp-verifiedhumancert.git\n",[974,990,992,996],{"class":976,"line":991},2,[974,993,995],{"class":994},"s2Zo4","cd",[974,997,998],{"class":984}," mcp-verifiedhumancert\n",[974,1000,1002,1005],{"class":976,"line":1001},3,[974,1003,1004],{"class":980},"poetry",[974,1006,1007],{"class":984}," install\n",[962,1009,1011],{"id":1010},"_2-register-with-claude-code","2. Register with Claude Code",[729,1013,1014,1015,1018,1019,1022],{},"Add this to your ",[782,1016,1017],{},"~/.claude/settings.json"," or project ",[782,1020,1021],{},".claude/settings.local.json",":",[900,1024,1029],{"className":1025,"code":1026,"filename":1027,"language":1028,"meta":906,"style":906},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"mcpServers\": {\n    \"vhc\": {\n      \"command\": \"poetry\",\n      \"args\": [\"--directory\", \"/path/to/mcp-verifiedhumancert\", \"run\", \"python\", \"-m\", \"mcp_verifiedhumancert\"]\n    }\n  }\n}\n","claude_mcp_config.json","json",[782,1030,1031,1037,1054,1068,1092,1163,1169,1175],{"__ignoreMap":906},[974,1032,1033],{"class":976,"line":977},[974,1034,1036],{"class":1035},"sMK4o","{\n",[974,1038,1039,1042,1046,1049,1051],{"class":976,"line":991},[974,1040,1041],{"class":1035},"  \"",[974,1043,1045],{"class":1044},"spNyl","mcpServers",[974,1047,1048],{"class":1035},"\"",[974,1050,1022],{"class":1035},[974,1052,1053],{"class":1035}," {\n",[974,1055,1056,1059,1062,1064,1066],{"class":976,"line":1001},[974,1057,1058],{"class":1035},"    \"",[974,1060,1061],{"class":980},"vhc",[974,1063,1048],{"class":1035},[974,1065,1022],{"class":1035},[974,1067,1053],{"class":1035},[974,1069,1071,1074,1078,1080,1082,1085,1087,1089],{"class":976,"line":1070},4,[974,1072,1073],{"class":1035},"      \"",[974,1075,1077],{"class":1076},"sbssI","command",[974,1079,1048],{"class":1035},[974,1081,1022],{"class":1035},[974,1083,1084],{"class":1035}," \"",[974,1086,1004],{"class":984},[974,1088,1048],{"class":1035},[974,1090,1091],{"class":1035},",\n",[974,1093,1095,1097,1100,1102,1104,1107,1109,1112,1114,1117,1119,1122,1124,1126,1128,1131,1133,1135,1137,1140,1142,1144,1146,1149,1151,1153,1155,1158,1160],{"class":976,"line":1094},5,[974,1096,1073],{"class":1035},[974,1098,1099],{"class":1076},"args",[974,1101,1048],{"class":1035},[974,1103,1022],{"class":1035},[974,1105,1106],{"class":1035}," [",[974,1108,1048],{"class":1035},[974,1110,1111],{"class":984},"--directory",[974,1113,1048],{"class":1035},[974,1115,1116],{"class":1035},",",[974,1118,1084],{"class":1035},[974,1120,1121],{"class":984},"/path/to/mcp-verifiedhumancert",[974,1123,1048],{"class":1035},[974,1125,1116],{"class":1035},[974,1127,1084],{"class":1035},[974,1129,1130],{"class":984},"run",[974,1132,1048],{"class":1035},[974,1134,1116],{"class":1035},[974,1136,1084],{"class":1035},[974,1138,1139],{"class":984},"python",[974,1141,1048],{"class":1035},[974,1143,1116],{"class":1035},[974,1145,1084],{"class":1035},[974,1147,1148],{"class":984},"-m",[974,1150,1048],{"class":1035},[974,1152,1116],{"class":1035},[974,1154,1084],{"class":1035},[974,1156,1157],{"class":984},"mcp_verifiedhumancert",[974,1159,1048],{"class":1035},[974,1161,1162],{"class":1035},"]\n",[974,1164,1166],{"class":976,"line":1165},6,[974,1167,1168],{"class":1035},"    }\n",[974,1170,1172],{"class":976,"line":1171},7,[974,1173,1174],{"class":1035},"  }\n",[974,1176,1178],{"class":976,"line":1177},8,[974,1179,1180],{"class":1035},"}\n",[962,1182,1184],{"id":1183},"_3-start-using-it","3. Start using it",[729,1186,1187],{},"That's it. Ask Claude anything about the VHC registry:",[845,1189,1190,1195,1200],{},[848,1191,1192],{},[851,1193,1194],{},"\"Look up cert number VH-2026-000001\"",[848,1196,1197],{},[851,1198,1199],{},"\"What are the current VHC pricing tiers?\"",[848,1201,1202],{},[851,1203,1204],{},"\"How many tracks are certified on the platform?\"",[729,1206,1207],{},[915,1208],{"alt":1209,"src":1210,"width":1211},"Looking up cert VH-2026-000001 in Claude Code","/images/blog/musictechlab_blog_vhc_mcp_cert_lookup.webp",600,[729,1213,1214],{},[915,1215],{"alt":1216,"src":1217,"width":1211},"Querying VHC pricing tiers in Claude Code","/images/blog/musictechlab_blog_vhc_mcp_pricing.webp",[750,1219,1221],{"id":1220},"under-the-hood","Under the hood",[729,1223,1224,1225,1230],{},"The server is built with ",[733,1226,1229],{"href":1227,"rel":1228},"https://github.com/modelcontextprotocol/python-sdk",[737],"FastMCP",", the Python SDK for the Model Context Protocol. The architecture is straightforward:",[845,1232,1233,1241,1249],{},[848,1234,1235,1240],{},[883,1236,1237],{},[782,1238,1239],{},"client.py"," - a thin HTTP wrapper around the VHC REST API using httpx",[848,1242,1243,1248],{},[883,1244,1245],{},[782,1246,1247],{},"server.py"," - six tool definitions that call the client and return JSON",[848,1250,1251,1254],{},[883,1252,1253],{},"Tests"," - full test coverage using respx for HTTP mocking",[900,1256,1259],{"className":1257,"code":1258,"filename":1247,"language":1139,"meta":906,"style":906},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@mcp.tool()\ndef vhc_verify_isrc(isrc: str) -> str:\n    \"\"\"Verify a human-made music certification by ISRC code.\"\"\"\n    result = client.verify_by_isrc(isrc)\n    return json.dumps(result, indent=2, ensure_ascii=False)\n",[782,1260,1261,1277,1308,1321,1345],{"__ignoreMap":906},[974,1262,1263,1266,1269,1271,1274],{"class":976,"line":977},[974,1264,1265],{"class":1035},"@",[974,1267,1268],{"class":994},"mcp",[974,1270,748],{"class":1035},[974,1272,1273],{"class":994},"tool",[974,1275,1276],{"class":1035},"()\n",[974,1278,1279,1282,1285,1288,1292,1294,1297,1300,1303,1305],{"class":976,"line":991},[974,1280,1281],{"class":1044},"def",[974,1283,1284],{"class":994}," vhc_verify_isrc",[974,1286,1287],{"class":1035},"(",[974,1289,1291],{"class":1290},"sHdIc","isrc",[974,1293,1022],{"class":1035},[974,1295,1296],{"class":980}," str",[974,1298,1299],{"class":1035},")",[974,1301,1302],{"class":1035}," ->",[974,1304,1296],{"class":980},[974,1306,1307],{"class":1035},":\n",[974,1309,1310,1314,1318],{"class":976,"line":1001},[974,1311,1313],{"class":1312},"s7zQu","    \"\"\"",[974,1315,1317],{"class":1316},"sHwdD","Verify a human-made music certification by ISRC code.",[974,1319,1320],{"class":1312},"\"\"\"\n",[974,1322,1323,1327,1330,1333,1335,1338,1340,1342],{"class":976,"line":1070},[974,1324,1326],{"class":1325},"sTEyZ","    result ",[974,1328,1329],{"class":1035},"=",[974,1331,1332],{"class":1325}," client",[974,1334,748],{"class":1035},[974,1336,1337],{"class":994},"verify_by_isrc",[974,1339,1287],{"class":1035},[974,1341,1291],{"class":994},[974,1343,1344],{"class":1035},")\n",[974,1346,1347,1350,1353,1355,1358,1360,1363,1365,1368,1370,1373,1375,1378],{"class":976,"line":1094},[974,1348,1349],{"class":1312},"    return",[974,1351,1352],{"class":1325}," json",[974,1354,748],{"class":1035},[974,1356,1357],{"class":994},"dumps",[974,1359,1287],{"class":1035},[974,1361,1362],{"class":994},"result",[974,1364,1116],{"class":1035},[974,1366,1367],{"class":1290}," indent",[974,1369,1329],{"class":1035},[974,1371,1372],{"class":1076},"2",[974,1374,1116],{"class":1035},[974,1376,1377],{"class":1290}," ensure_ascii",[974,1379,1380],{"class":1035},"=False)\n",[900,1382,1384],{"className":1257,"code":1383,"filename":1239,"language":1139,"meta":906,"style":906},"def verify_by_isrc(isrc: str) -> dict:\n    \"\"\"Verify a certification by ISRC code.\"\"\"\n    return _get(\"/api/v1/verify\", params={\"isrc\": isrc})\n",[782,1385,1386,1410,1419],{"__ignoreMap":906},[974,1387,1388,1390,1393,1395,1397,1399,1401,1403,1405,1408],{"class":976,"line":977},[974,1389,1281],{"class":1044},[974,1391,1392],{"class":994}," verify_by_isrc",[974,1394,1287],{"class":1035},[974,1396,1291],{"class":1290},[974,1398,1022],{"class":1035},[974,1400,1296],{"class":980},[974,1402,1299],{"class":1035},[974,1404,1302],{"class":1035},[974,1406,1407],{"class":980}," dict",[974,1409,1307],{"class":1035},[974,1411,1412,1414,1417],{"class":976,"line":991},[974,1413,1313],{"class":1312},[974,1415,1416],{"class":1316},"Verify a certification by ISRC code.",[974,1418,1320],{"class":1312},[974,1420,1421,1423,1426,1428,1430,1433,1435,1437,1440,1443,1445,1447,1449,1451,1454],{"class":976,"line":1001},[974,1422,1349],{"class":1312},[974,1424,1425],{"class":994}," _get",[974,1427,1287],{"class":1035},[974,1429,1048],{"class":1035},[974,1431,1432],{"class":984},"/api/v1/verify",[974,1434,1048],{"class":1035},[974,1436,1116],{"class":1035},[974,1438,1439],{"class":1290}," params",[974,1441,1442],{"class":1035},"={",[974,1444,1048],{"class":1035},[974,1446,1291],{"class":984},[974,1448,1048],{"class":1035},[974,1450,1022],{"class":1035},[974,1452,1453],{"class":994}," isrc",[974,1455,1456],{"class":1035},"})\n",[729,1458,1459],{},"The entire server is under 200 lines of Python. That's intentional. MCP servers should be thin wrappers, not application frameworks.",[1461,1462,1463],"note",{},[729,1464,1465],{},"The project uses Poetry for dependency management, Ruff for linting and formatting, and pytest with respx for testing. CI runs on GitHub Actions.",[750,1467,1469],{"id":1468},"what-we-learned-building-mcp-servers","What we learned building MCP servers",[729,1471,1472,1473,1478,1479,1482],{},"This is the third MCP server we have open-sourced at MusicTech Lab (after ",[733,1474,1477],{"href":1475,"rel":1476},"https://github.com/musictechlab/signnow-mcp",[737],"signnow-mcp"," and ",[733,1480,875],{"href":873,"rel":1481},[737],"). A few patterns have emerged:",[878,1484,1485,1491,1497,1503],{},[848,1486,1487,1490],{},[883,1488,1489],{},"Keep servers focused."," One server per domain. Don't bundle unrelated tools into a single server.",[848,1492,1493,1496],{},[883,1494,1495],{},"Separate the client from the server."," The HTTP client should be testable independently of the MCP layer.",[848,1498,1499,1502],{},[883,1500,1501],{},"Return JSON, not prose."," Let Claude format the output for the user. The server's job is to provide structured data.",[848,1504,1505,1508,1509,1512],{},[883,1506,1507],{},"Skip authentication when you can."," Public APIs make MCP servers trivial to set up. No ",[782,1510,1511],{},".env"," files, no OAuth flows, no token management.",[750,1514,1516],{"id":1515},"open-source","Open source",[729,1518,1519,1520,1524],{},"The full source code is on GitHub: ",[733,1521,1523],{"href":745,"rel":1522},[737],"musictechlab/mcp-verifiedhumancert",". MIT licensed. Contributions welcome.",[729,1526,1527],{},"If you're building MCP servers for the music industry, or if you're using Verified Human Cert and want to integrate it into your tooling, we'd love to hear from you.",[750,1529,1531],{"id":1530},"related-resources","Related resources",[845,1533,1534,1541,1548,1554,1560],{},[848,1535,1536,1540],{},[733,1537,1539],{"href":745,"rel":1538},[737],"GitHub: musictechlab/mcp-verifiedhumancert"," - the source code",[848,1542,1543,1547],{},[733,1544,1546],{"href":873,"rel":1545},[737],"GitHub: musictechlab/mcp-metadata"," - audio metadata MCP server",[848,1549,1550,1553],{},[733,1551,1552],{"href":619},"SignNow MCP Server: E-Signatures from Claude Code"," - our previous MCP server article",[848,1555,1556,1559],{},[733,1557,738],{"href":735,"rel":1558},[737]," - the certification platform",[848,1561,1562,1567],{},[733,1563,1566],{"href":1564,"rel":1565},"https://modelcontextprotocol.io/",[737],"Model Context Protocol"," - the MCP standard",[1569,1570,1571],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":906,"searchDepth":991,"depth":991,"links":1573},[1574,1575,1576,1577,1582,1583,1584,1585],{"id":752,"depth":991,"text":753},{"id":866,"depth":991,"text":867},{"id":927,"depth":991,"text":928},{"id":959,"depth":991,"text":960,"children":1578},[1579,1580,1581],{"id":964,"depth":1001,"text":965},{"id":1010,"depth":1001,"text":1011},{"id":1183,"depth":1001,"text":1184},{"id":1220,"depth":991,"text":1221},{"id":1468,"depth":991,"text":1469},{"id":1515,"depth":991,"text":1516},{"id":1530,"depth":991,"text":1531},"software-development",null,"2026-04-28T00:00:00.000Z","We open-sourced an MCP server that queries the Verified Human Cert registry. Verify human-made music certifications by ISRC, artist, track, or cert number directly from Claude Code.","md",[1592,1595,1598,1601,1604],{"question":1593,"answer":1594},"What is Verified Human Cert?","Verified Human Cert is a registry that certifies music tracks as human-made. Artists and labels register their tracks to receive proof that the music was created by humans, not generated by artificial intelligence.",{"question":1596,"answer":1597},"What is the mcp-verifiedhumancert server?","It is an open-source Model Context Protocol server that connects the Verified Human Cert public API to Claude Code and other MCP-compatible clients. It lets you verify certifications, browse the registry, and check pricing directly from your terminal.",{"question":1599,"answer":1600},"Do I need an API key to use this?","No. The server queries the public VHC API, which does not require authentication for read-only operations like verifying certifications and browsing the registry.",{"question":1602,"answer":1603},"What is the multi-agent workflow?","By combining mcp-verifiedhumancert with mcp-metadata, Claude can read the ISRC code embedded in an audio file and then verify its certification status in a single conversation. Two MCP servers, two agents, one orchestrator.",{"question":1605,"answer":1606},"Can I use this with clients other than Claude Code?","Yes. Any MCP-compatible client can use this server. The MCP protocol is open and supported by a growing number of tools.",true,12,{"src":1610,"credit":1611},"/images/blog/musictechlab_blog_verified_human_cert_mcp.webp","Photo by [Erika Giraud](https://unsplash.com/@erikasayssmile) on [Unsplash](https://unsplash.com/photos/JXA_BaeaCgM)",{"enabled":1607,"items":1613},[1614,1617,1619],{"text":1615,"icon":1616},"The mcp-verifiedhumancert server exposes 6 tools for querying the Verified Human Cert registry.","i-lucide-terminal",{"text":1618,"icon":950},"Combine with mcp-metadata to read ISRC from audio files and verify certifications automatically.",{"text":1620,"icon":1621},"No API key required. The server queries the public VHC registry at verifiedhumancert.com.","i-lucide-unlock",{},{"title":1624,"description":1625},"Verified Human Cert MCP Server | MusicTech Lab","Open-source MCP server for verifying human-made music certifications. Query by ISRC, artist, track, or cert number from Claude Code.",[1268,1515,1627,1628,1629,1630,1291],"verified-human-cert","music-certification","claude-code","multi-agent","qiI5dyjkEO9gWuMmxfqpQh8or1D6HZ9AxEMrcx-IDGQ",[1633,1635],{"title":570,"path":571,"stem":572,"description":1634,"children":-1},"Five reasons why the Kanban board methodology improves team productivity. Learn how visual task management helps prioritize work and adapt to changes.",{"title":578,"path":579,"stem":580,"description":1636,"children":-1},"A practical comparison of GitHub Actions vs TravisCI with a step-by-step migration guide. Learn why we switched and how to move your CI/CD pipeline smoothly.",[1638,2444,2468,2489],{"id":1639,"title":618,"authors":1640,"badge":1643,"body":1644,"category":1586,"client":1587,"date":2408,"description":2409,"extension":1590,"faq":2410,"featured":1607,"featuredOrder":2217,"hidden":69,"image":2422,"keyTakeaways":2425,"meta":2435,"navigation":1607,"path":619,"seo":2436,"status":1587,"stem":620,"tags":2439,"teaser":1587,"__hash__":2443,"score":1094},"posts/blog/software-development/signnow-mcp-server-e-signatures-from-claude-code.md",[1641],{"name":718,"to":719,"avatar":1642},{"src":721},{"label":723,"color":724},{"type":726,"value":1645,"toc":2394},[1646,1653,1656,1663,1667,1674,1677,1679,1682,1804,1807,1811,1814,1820,1831,1833,1837,1871,1897,1901,1944,1948,1951,1994,1997,2229,2238,2242,2245,2275,2279,2288,2291,2296,2300,2303,2348,2354,2356,2391],[729,1647,1648,1649,1652],{},"A few weeks ago we published a deep dive on ",[733,1650,1651],{"href":543},"integrating SignNow e-signatures into a Django application",". That article covered the full server-side integration — OAuth2, document uploads, Celery tasks, webhooks — everything you need for a production signing workflow.",[729,1654,1655],{},"But we kept coming back to the same thought: what if you didn't need a web app at all? What if you could upload a contract, send it for signing, and check its status without ever leaving your terminal?",[729,1657,1658,1659,1662],{},"That's exactly what we built. Today we're open-sourcing ",[733,1660,1477],{"href":1475,"rel":1661},[737]," — a Model Context Protocol server that brings airSlate SignNow's e-signature capabilities directly into Claude Code.",[750,1664,1666],{"id":1665},"what-is-mcp","What is MCP?",[729,1668,1669,1670,1673],{},"The ",[733,1671,1566],{"href":1564,"rel":1672},[737]," (MCP) is an open standard that lets AI assistants like Claude interact with external tools and services. Think of it as a plugin system — you register an MCP server, and Claude gains new abilities.",[729,1675,1676],{},"In our case, those abilities are e-signatures.",[750,1678,753],{"id":752},[729,1680,1681],{},"The signnow-mcp server exposes 11 tools that cover the full document lifecycle:",[758,1683,1684,1692],{},[761,1685,1686],{},[764,1687,1688,1690],{},[767,1689,769],{},[767,1691,772],{},[774,1693,1694,1704,1714,1724,1734,1744,1754,1764,1774,1784,1794],{},[764,1695,1696,1701],{},[779,1697,1698],{},[782,1699,1700],{},"upload_document",[779,1702,1703],{},"Upload a PDF to SignNow",[764,1705,1706,1711],{},[779,1707,1708],{},[782,1709,1710],{},"get_document",[779,1712,1713],{},"Get document details and signing status",[764,1715,1716,1721],{},[779,1717,1718],{},[782,1719,1720],{},"list_documents",[779,1722,1723],{},"List all documents in the account",[764,1725,1726,1731],{},[779,1727,1728],{},[782,1729,1730],{},"download_signed_document",[779,1732,1733],{},"Download a signed PDF locally",[764,1735,1736,1741],{},[779,1737,1738],{},[782,1739,1740],{},"send_signing_invite",[779,1742,1743],{},"Send a freeform e-signature invite",[764,1745,1746,1751],{},[779,1747,1748],{},[782,1749,1750],{},"send_role_based_invite",[779,1752,1753],{},"Send a role-based invite with field assignments",[764,1755,1756,1761],{},[779,1757,1758],{},[782,1759,1760],{},"cancel_invite",[779,1762,1763],{},"Cancel pending signing invites",[764,1765,1766,1771],{},[779,1767,1768],{},[782,1769,1770],{},"add_signature_field",[779,1772,1773],{},"Add a signature field to a document",[764,1775,1776,1781],{},[779,1777,1778],{},[782,1779,1780],{},"list_templates",[779,1782,1783],{},"List all document templates",[764,1785,1786,1791],{},[779,1787,1788],{},[782,1789,1790],{},"create_from_template",[779,1792,1793],{},"Create a document from a template",[764,1795,1796,1801],{},[779,1797,1798],{},[782,1799,1800],{},"register_webhook",[779,1802,1803],{},"Register a webhook for document events",[729,1805,1806],{},"Once connected, you interact with these tools through natural language. No API calls, no curl commands, no switching between browser tabs.",[750,1808,1810],{"id":1809},"how-it-looks-in-practice","How it looks in practice",[729,1812,1813],{},"Here's what it looks like when you ask Claude Code to list your SignNow documents:",[729,1815,1816],{},[915,1817],{"alt":1818,"src":1819},"SignNow MCP in action — listing documents from Claude Code","https://raw.githubusercontent.com/musictechlab/signnow-mcp/main/docs/signnow-mcp-demo.webp",[729,1821,1822,1823,1825,1826,1830],{},"Claude calls the ",[782,1824,1720],{}," tool behind the scenes and presents the results in a clean table. You can then follow up with natural language — \"download the first one\", \"send document #2 to ",[733,1827,1829],{"href":1828},"mailto:john@example.com","john@example.com"," for signing\", or \"what's the status of the deposit confirmation?\".",[750,1832,960],{"id":959},[962,1834,1836],{"id":1835},"_1-get-signnow-api-credentials","1. Get SignNow API credentials",[878,1838,1839,1848,1859,1868],{},[848,1840,1841,1842,1847],{},"Create a ",[733,1843,1846],{"href":1844,"rel":1845},"https://www.signnow.com/",[737],"SignNow"," account",[848,1849,1850,1851,1854,1855,1858],{},"Go to ",[883,1852,1853],{},"API"," > ",[883,1856,1857],{},"Applications"," and create an application",[848,1860,1861,1862,1478,1865],{},"Note your ",[782,1863,1864],{},"client_id",[782,1866,1867],{},"client_secret",[848,1869,1870],{},"Base64-encode them:",[900,1872,1874],{"className":968,"code":1873,"language":970,"meta":906,"style":906},"echo -n \"client_id:client_secret\" | base64\n",[782,1875,1876],{"__ignoreMap":906},[974,1877,1878,1881,1884,1886,1889,1891,1894],{"class":976,"line":977},[974,1879,1880],{"class":994},"echo",[974,1882,1883],{"class":984}," -n",[974,1885,1084],{"class":1035},[974,1887,1888],{"class":984},"client_id:client_secret",[974,1890,1048],{"class":1035},[974,1892,1893],{"class":1035}," |",[974,1895,1896],{"class":980}," base64\n",[962,1898,1900],{"id":1899},"_2-clone-and-install","2. Clone and install",[900,1902,1904],{"className":968,"code":1903,"language":970,"meta":906,"style":906},"git clone https://github.com/musictechlab/signnow-mcp.git\ncd signnow-mcp\ncp .env.example .env\n# Edit .env with your credentials\npoetry install\n",[782,1905,1906,1915,1922,1933,1938],{"__ignoreMap":906},[974,1907,1908,1910,1912],{"class":976,"line":977},[974,1909,981],{"class":980},[974,1911,985],{"class":984},[974,1913,1914],{"class":984}," https://github.com/musictechlab/signnow-mcp.git\n",[974,1916,1917,1919],{"class":976,"line":991},[974,1918,995],{"class":994},[974,1920,1921],{"class":984}," signnow-mcp\n",[974,1923,1924,1927,1930],{"class":976,"line":1001},[974,1925,1926],{"class":980},"cp",[974,1928,1929],{"class":984}," .env.example",[974,1931,1932],{"class":984}," .env\n",[974,1934,1935],{"class":976,"line":1070},[974,1936,1937],{"class":1316},"# Edit .env with your credentials\n",[974,1939,1940,1942],{"class":976,"line":1094},[974,1941,1004],{"class":980},[974,1943,1007],{"class":984},[962,1945,1947],{"id":1946},"_3-register-with-claude-code","3. Register with Claude Code",[729,1949,1950],{},"The quickest way:",[900,1952,1954],{"className":968,"code":1953,"language":970,"meta":906,"style":906},"claude mcp add signnow -- poetry -C /path/to/signnow-mcp run python -m signnow_mcp.server\n",[782,1955,1956],{"__ignoreMap":906},[974,1957,1958,1961,1964,1967,1970,1973,1976,1979,1982,1985,1988,1991],{"class":976,"line":977},[974,1959,1960],{"class":980},"claude",[974,1962,1963],{"class":984}," mcp",[974,1965,1966],{"class":984}," add",[974,1968,1969],{"class":984}," signnow",[974,1971,1972],{"class":984}," --",[974,1974,1975],{"class":984}," poetry",[974,1977,1978],{"class":984}," -C",[974,1980,1981],{"class":984}," /path/to/signnow-mcp",[974,1983,1984],{"class":984}," run",[974,1986,1987],{"class":984}," python",[974,1989,1990],{"class":984}," -m",[974,1992,1993],{"class":984}," signnow_mcp.server\n",[729,1995,1996],{},"Or add it manually to your MCP configuration:",[900,1998,2000],{"className":1025,"code":1999,"filename":1027,"language":1028,"meta":906,"style":906},"{\n  \"signnow\": {\n    \"type\": \"stdio\",\n    \"command\": \"poetry\",\n    \"args\": [\"-C\", \"/path/to/signnow-mcp\", \"run\", \"python\", \"-m\", \"signnow_mcp.server\"],\n    \"env\": {\n      \"SIGNNOW_API_BASE_URL\": \"https://api.signnow.com\",\n      \"SIGNNOW_BASIC_AUTH\": \"your-base64-encoded-credentials\",\n      \"SIGNNOW_USERNAME\": \"your-email@example.com\",\n      \"SIGNNOW_PASSWORD\": \"your-password\"\n    }\n  }\n}\n",[782,2001,2002,2006,2019,2039,2057,2121,2134,2154,2174,2195,2215,2220,2224],{"__ignoreMap":906},[974,2003,2004],{"class":976,"line":977},[974,2005,1036],{"class":1035},[974,2007,2008,2010,2013,2015,2017],{"class":976,"line":991},[974,2009,1041],{"class":1035},[974,2011,2012],{"class":1044},"signnow",[974,2014,1048],{"class":1035},[974,2016,1022],{"class":1035},[974,2018,1053],{"class":1035},[974,2020,2021,2023,2026,2028,2030,2032,2035,2037],{"class":976,"line":1001},[974,2022,1058],{"class":1035},[974,2024,2025],{"class":980},"type",[974,2027,1048],{"class":1035},[974,2029,1022],{"class":1035},[974,2031,1084],{"class":1035},[974,2033,2034],{"class":984},"stdio",[974,2036,1048],{"class":1035},[974,2038,1091],{"class":1035},[974,2040,2041,2043,2045,2047,2049,2051,2053,2055],{"class":976,"line":1070},[974,2042,1058],{"class":1035},[974,2044,1077],{"class":980},[974,2046,1048],{"class":1035},[974,2048,1022],{"class":1035},[974,2050,1084],{"class":1035},[974,2052,1004],{"class":984},[974,2054,1048],{"class":1035},[974,2056,1091],{"class":1035},[974,2058,2059,2061,2063,2065,2067,2069,2071,2074,2076,2078,2080,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2103,2105,2107,2109,2111,2113,2116,2118],{"class":976,"line":1094},[974,2060,1058],{"class":1035},[974,2062,1099],{"class":980},[974,2064,1048],{"class":1035},[974,2066,1022],{"class":1035},[974,2068,1106],{"class":1035},[974,2070,1048],{"class":1035},[974,2072,2073],{"class":984},"-C",[974,2075,1048],{"class":1035},[974,2077,1116],{"class":1035},[974,2079,1084],{"class":1035},[974,2081,2082],{"class":984},"/path/to/signnow-mcp",[974,2084,1048],{"class":1035},[974,2086,1116],{"class":1035},[974,2088,1084],{"class":1035},[974,2090,1130],{"class":984},[974,2092,1048],{"class":1035},[974,2094,1116],{"class":1035},[974,2096,1084],{"class":1035},[974,2098,1139],{"class":984},[974,2100,1048],{"class":1035},[974,2102,1116],{"class":1035},[974,2104,1084],{"class":1035},[974,2106,1148],{"class":984},[974,2108,1048],{"class":1035},[974,2110,1116],{"class":1035},[974,2112,1084],{"class":1035},[974,2114,2115],{"class":984},"signnow_mcp.server",[974,2117,1048],{"class":1035},[974,2119,2120],{"class":1035},"],\n",[974,2122,2123,2125,2128,2130,2132],{"class":976,"line":1165},[974,2124,1058],{"class":1035},[974,2126,2127],{"class":980},"env",[974,2129,1048],{"class":1035},[974,2131,1022],{"class":1035},[974,2133,1053],{"class":1035},[974,2135,2136,2138,2141,2143,2145,2147,2150,2152],{"class":976,"line":1171},[974,2137,1073],{"class":1035},[974,2139,2140],{"class":1076},"SIGNNOW_API_BASE_URL",[974,2142,1048],{"class":1035},[974,2144,1022],{"class":1035},[974,2146,1084],{"class":1035},[974,2148,2149],{"class":984},"https://api.signnow.com",[974,2151,1048],{"class":1035},[974,2153,1091],{"class":1035},[974,2155,2156,2158,2161,2163,2165,2167,2170,2172],{"class":976,"line":1177},[974,2157,1073],{"class":1035},[974,2159,2160],{"class":1076},"SIGNNOW_BASIC_AUTH",[974,2162,1048],{"class":1035},[974,2164,1022],{"class":1035},[974,2166,1084],{"class":1035},[974,2168,2169],{"class":984},"your-base64-encoded-credentials",[974,2171,1048],{"class":1035},[974,2173,1091],{"class":1035},[974,2175,2177,2179,2182,2184,2186,2188,2191,2193],{"class":976,"line":2176},9,[974,2178,1073],{"class":1035},[974,2180,2181],{"class":1076},"SIGNNOW_USERNAME",[974,2183,1048],{"class":1035},[974,2185,1022],{"class":1035},[974,2187,1084],{"class":1035},[974,2189,2190],{"class":984},"your-email@example.com",[974,2192,1048],{"class":1035},[974,2194,1091],{"class":1035},[974,2196,2198,2200,2203,2205,2207,2209,2212],{"class":976,"line":2197},10,[974,2199,1073],{"class":1035},[974,2201,2202],{"class":1076},"SIGNNOW_PASSWORD",[974,2204,1048],{"class":1035},[974,2206,1022],{"class":1035},[974,2208,1084],{"class":1035},[974,2210,2211],{"class":984},"your-password",[974,2213,2214],{"class":1035},"\"\n",[974,2216,2218],{"class":976,"line":2217},11,[974,2219,1168],{"class":1035},[974,2221,2222],{"class":976,"line":1608},[974,2223,1174],{"class":1035},[974,2225,2227],{"class":976,"line":2226},13,[974,2228,1180],{"class":1035},[920,2230,2231],{},[729,2232,2233,2234,2237],{},"Use the sandbox environment (",[782,2235,2236],{},"https://api-eval.signnow.com",") for testing. SignNow provides 2,000 free signature invites in sandbox mode.",[962,2239,2241],{"id":2240},"_4-start-using-it","4. Start using it",[729,2243,2244],{},"Once configured, just talk to Claude:",[845,2246,2247,2255,2260,2265,2270],{},[848,2248,2249],{},[851,2250,2251,2252,2254],{},"\"Upload contract.pdf to SignNow and send it to ",[733,2253,1829],{"href":1828}," for signing\"",[848,2256,2257],{},[851,2258,2259],{},"\"Check the signing status of document abc123\"",[848,2261,2262],{},[851,2263,2264],{},"\"List all my SignNow documents\"",[848,2266,2267],{},[851,2268,2269],{},"\"Download the signed version of the NDA\"",[848,2271,2272],{},[851,2273,2274],{},"\"Create a new document from the deposit template and send it to the client\"",[750,2276,2278],{"id":2277},"why-we-built-this","Why we built this",[729,2280,2281,2282,2287],{},"At MusicTech Lab, we use SignNow for sending contracts, NDAs, and deposit confirmations as part of our ",[733,2283,2286],{"href":2284,"rel":2285},"https://beatbuddy.pro",[737],"BeatBuddy"," onboarding flow. Our Django integration handles the automated pipeline, but there are always ad-hoc tasks — checking a document's status, resending an invite, downloading a signed copy for the records.",[729,2289,2290],{},"Before the MCP server, that meant logging into the SignNow dashboard or writing one-off API calls. Now it's a single sentence in Claude Code.",[1461,2292,2293],{},[729,2294,2295],{},"This pattern — wrapping a third-party API as an MCP server — works for any service with a REST API. If you find yourself repeatedly switching to a web dashboard to do something, consider building an MCP server for it.",[750,2297,2299],{"id":2298},"sandbox-vs-production","Sandbox vs. production",[729,2301,2302],{},"SignNow provides separate environments for testing and production:",[758,2304,2305,2318],{},[761,2306,2307],{},[764,2308,2309,2312,2315],{},[767,2310,2311],{},"Environment",[767,2313,2314],{},"API URL",[767,2316,2317],{},"App URL",[774,2319,2320,2334],{},[764,2321,2322,2325,2329],{},[779,2323,2324],{},"Sandbox",[779,2326,2327],{},[782,2328,2236],{},[779,2330,2331],{},[782,2332,2333],{},"https://app-eval.signnow.com",[764,2335,2336,2339,2343],{},[779,2337,2338],{},"Production",[779,2340,2341],{},[782,2342,2149],{},[779,2344,2345],{},[782,2346,2347],{},"https://app.signnow.com",[2349,2350,2351],"warning",{},[729,2352,2353],{},"Never use production credentials for development. The sandbox environment is free and gives you 2,000 signature invites to test with.",[750,2355,1531],{"id":1530},[845,2357,2358,2365,2370,2376,2383],{},[848,2359,2360,2364],{},[733,2361,2363],{"href":1475,"rel":2362},[737],"GitHub: musictechlab/signnow-mcp"," — the source code",[848,2366,2367,2369],{},[733,2368,542],{"href":543}," — our deep dive on the server-side integration",[848,2371,2372,2375],{},[733,2373,2374],{"href":165},"Querying Bandcamp Revenue with Natural Language"," — another MCP server we built, this time for music revenue analytics",[848,2377,2378,2382],{},[733,2379,2381],{"href":1564,"rel":2380},[737],"Model Context Protocol specification"," — the MCP standard",[848,2384,2385,2390],{},[733,2386,2389],{"href":2387,"rel":2388},"https://docs.signnow.com/",[737],"SignNow API documentation"," — official API reference",[1569,2392,2393],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":906,"searchDepth":991,"depth":991,"links":2395},[2396,2397,2398,2399,2405,2406,2407],{"id":1665,"depth":991,"text":1666},{"id":752,"depth":991,"text":753},{"id":1809,"depth":991,"text":1810},{"id":959,"depth":991,"text":960,"children":2400},[2401,2402,2403,2404],{"id":1835,"depth":1001,"text":1836},{"id":1899,"depth":1001,"text":1900},{"id":1946,"depth":1001,"text":1947},{"id":2240,"depth":1001,"text":2241},{"id":2277,"depth":991,"text":2278},{"id":2298,"depth":991,"text":2299},{"id":1530,"depth":991,"text":1531},"2026-03-12T00:00:00.000Z","We open-sourced an MCP server that brings SignNow e-signatures into Claude Code. Upload documents, send signing invites, track status, and download signed PDFs — all without leaving your terminal.",[2411,2414,2417,2420],{"question":2412,"answer":2413},"What is the SignNow MCP server?","It is an open-source Model Context Protocol server that connects airSlate SignNow's e-signature API to Claude Code and other MCP-compatible clients. It lets you manage documents, send signing invites, and download signed PDFs directly from your terminal.",{"question":2415,"answer":2416},"Is this an official SignNow product?","No. This is a community project built by MusicTech Lab. It is not affiliated with or endorsed by airSlate SignNow.",{"question":2418,"answer":2419},"What tools does the server provide?","The server exposes 11 tools: upload_document, get_document, list_documents, download_signed_document, send_signing_invite, send_role_based_invite, cancel_invite, add_signature_field, list_templates, create_from_template, and register_webhook.",{"question":1605,"answer":2421},"Yes. Any MCP-compatible client can use this server. Claude Code is the primary client we built it for, but the MCP protocol is open and supported by a growing number of tools.",{"src":2423,"credit":2424},"/images/blog/musictechlab_blog_signnow_mcp_server.webp","Photo by [Vitaly Gariev](https://unsplash.com/@silverkblack) on [Unsplash](https://unsplash.com/photos/iPheGw7_UaI)",{"enabled":1607,"items":2426},[2427,2429,2432],{"text":2428,"icon":1616},"The signnow-mcp server exposes 11 tools covering the full document signing lifecycle.",{"text":2430,"icon":2431},"Natural language replaces API calls: upload, sign, and track documents from the terminal.","i-lucide-sparkles",{"text":2433,"icon":2434},"SignNow sandbox provides 2,000 free signature invites for development and testing.","i-lucide-package",{},{"title":2437,"description":2438},"SignNow MCP Server for Claude Code | MusicTech Lab","Open-source MCP server for SignNow e-signatures. Upload, sign, and manage documents from Claude Code or any MCP client.",[1268,1515,2440,2012,1629,2441,2442],"e-signatures","developer-tools","automation","rJ-D5aky3r9E1UyuToU6W-lLu8q7MVunwz7LPwgR0Y0",{"id":2445,"title":410,"authors":2446,"badge":1587,"body":2450,"category":1586,"client":1587,"date":2454,"description":2455,"extension":1590,"faq":1587,"featured":69,"featuredOrder":1587,"hidden":69,"image":2456,"keyTakeaways":1587,"meta":2458,"navigation":1607,"path":411,"seo":2459,"status":2460,"stem":412,"tags":2461,"teaser":2466,"__hash__":2467,"score":991},"posts/blog/software-development/cross-platform-problem-for-creators.md",[2447],{"name":2448,"to":719,"avatar":2449},"Mariusz Smenzyk",{"src":721},{"type":726,"value":2451,"toc":2452},[],{"title":906,"searchDepth":991,"depth":991,"links":2453},[],"2026-05-22T00:00:00.000Z","Ideas happen anywhere — rehearsal room, commute, studio. But syncing between devices is manual and error-prone. Offline support and conflict resolution are nonexistent.",{"src":2457},"/images/blog/musictechlab_blog_cross-platform-creators.webp",{},{"title":410,"description":2455},"upcoming",[2462,2463,2464,2465],"music-tech","mobile","cross-platform","sync","We'll explore the gap between capturing an idea on your phone and working on it in the studio — and why no tool has solved this properly yet.","lMykAOfi6ZnzrGoQwiTRS3ZIXCTLtU-rBei1PqZ3DLg",{"id":2469,"title":686,"authors":2470,"badge":1587,"body":2473,"category":1586,"client":1587,"date":2477,"description":2478,"extension":1590,"faq":1587,"featured":69,"featuredOrder":1587,"hidden":69,"image":2479,"keyTakeaways":1587,"meta":2481,"navigation":1607,"path":687,"seo":2482,"status":2460,"stem":688,"tags":2483,"teaser":2487,"__hash__":2488,"score":991},"posts/blog/software-development/why-daws-wrong-tool-for-starting-song.md",[2471],{"name":2448,"to":719,"avatar":2472},{"src":721},{"type":726,"value":2474,"toc":2475},[],{"title":906,"searchDepth":991,"depth":991,"links":2476},[],"2026-04-22T00:00:00.000Z","DAWs are great for production but kill the flow at the ideation stage. Creativity in music is cyclical and chaotic — current tools force a linear workflow that doesn't match.",{"src":2480},"/images/blog/musictechlab_blog_daw-wrong-tool.webp",{},{"title":686,"description":2478},[2462,2484,2485,2486],"creativity","daw","workflow","We'll argue that the gap between 'capture' and 'production' needs its own category of tools — and why forcing structure too early kills the best ideas.","4AI328U45cb7sh9Lft7JC-I6lSB50ygH-5rRIBXQD2w",{"id":2490,"title":694,"authors":2491,"badge":2494,"body":2496,"category":1586,"client":1587,"date":3603,"description":3604,"extension":1590,"faq":3605,"featured":69,"featuredOrder":1587,"hidden":69,"image":3615,"keyTakeaways":1587,"meta":3617,"navigation":1607,"path":695,"seo":3618,"status":1587,"stem":696,"tags":3621,"teaser":1587,"__hash__":3628,"score":991},"posts/blog/software-development/why-we-dont-build-chat-from-scratch.md",[2492],{"name":2448,"to":719,"avatar":2493},{"src":721},{"label":5,"color":2495},"#f59e0b",{"type":726,"value":2497,"toc":3590},[2498,2501,2505,2508,2511,2515,2518,2551,2554,2559,2563,2572,2575,2598,2601,2605,2613,2617,2637,2641,2648,2653,2656,2908,3217,3226,3231,3234,3439,3449,3455,3459,3492,3496,3499,3504,3518,3523,3534,3537,3541,3563,3567,3570,3573,3576,3587],[729,2499,2500],{},"\"We need a chat feature.\" Five words that sound simple but hide a massive engineering project underneath. We hear this request regularly from clients who are building platforms where users need to communicate. And every time, our answer is the same: we don't build chat from scratch. Here's why.",[750,2502,2504],{"id":2503},"the-request-that-sounds-simple","The request that sounds simple",[729,2506,2507],{},"A client comes to us with a platform idea. Users need to message each other. Maybe it's artists talking to managers, editors talking to sound designers, or customers talking to support. The feature request fits in one sentence: \"Add a chat.\"",[729,2509,2510],{},"From the outside, it looks straightforward. A text input, a send button, messages appearing on screen. How hard can it be?",[750,2512,2514],{"id":2513},"what-building-chat-actually-means","What building chat actually means",[729,2516,2517],{},"Very hard. Real-time messaging is one of those features that looks like 10% of the work but turns into 60% if you try to build it yourself. Here's what's hiding behind that simple chat bubble:",[933,2519,2522,2527,2531,2536,2541,2546],{"className":2520},[936,937,2521,939,940],"md:grid-cols-2",[942,2523],{"description":2524,"icon":2525,"title":2526},"Persistent connections, reconnection logic, heartbeats, connection state management across devices.","i-lucide-wifi","WebSocket Infrastructure",[942,2528],{"description":2529,"icon":945,"title":2530},"Message ordering, deduplication, retry logic. What happens when the network drops mid-message?","Delivery Guarantees",[942,2532],{"description":2533,"icon":2534,"title":2535},"History, search, pagination, attachments. Every message needs to be stored, indexed, and retrievable.","i-lucide-database","Message Storage",[942,2537],{"description":2538,"icon":2539,"title":2540},"Mobile push, desktop notifications, badge counts. Different platforms, different APIs.","i-lucide-bell","Push Notifications",[942,2542],{"description":2543,"icon":2544,"title":2545},"Who's online? Who read the message? When? This needs real-time sync across all connected clients.","i-lucide-eye","Read Receipts & Presence",[942,2547],{"description":2548,"icon":2549,"title":2550},"What works for 10 users breaks at 1,000. What works at 1,000 breaks at 100,000.","i-lucide-trending-up","Scaling",[729,2552,2553],{},"Companies like Slack, WhatsApp, and Discord have entire engineering teams dedicated to messaging infrastructure. That's their core product. When chat is just one feature in your app, spending months building and maintaining this infrastructure doesn't make sense.",[2349,2555,2556],{},[729,2557,2558],{},"Building real-time chat from scratch typically takes 3-6 months of dedicated engineering time. That's time and budget taken away from the features that make your product unique.",[750,2560,2562],{"id":2561},"the-smart-approach-managed-chat-services","The smart approach: managed chat services",[729,2564,2565,2566,2571],{},"Services like ",[733,2567,2570],{"href":2568,"rel":2569},"https://getstream.io",[737],"Stream",", Sendbird, and PubNub exist specifically to solve this problem. They provide battle-tested messaging infrastructure through APIs and SDKs. You get years of engineering in a single integration.",[729,2573,2574],{},"What a managed service gives you out of the box:",[845,2576,2577,2580,2583,2586,2589,2592,2595],{},[848,2578,2579],{},"Real-time message delivery with offline support",[848,2581,2582],{},"Message history, search, and threading",[848,2584,2585],{},"Read receipts and typing indicators",[848,2587,2588],{},"Unread counts and push notifications",[848,2590,2591],{},"File and image attachments",[848,2593,2594],{},"Moderation and content filtering",[848,2596,2597],{},"SDKs for web, iOS, Android, Flutter, React Native",[729,2599,2600],{},"The cost? A predictable monthly fee based on usage, instead of months of custom development and ongoing maintenance.",[750,2602,2604],{"id":2603},"how-we-did-it-the-artist-suite","How we did it: The Artist Suite",[729,2606,2607,2608,2612],{},"We recently built ",[733,2609,2611],{"href":2610},"/case-studies","The Artist Suite",", a platform connecting music artists with industry professionals. One of the core requirements was direct messaging between users. Artists needed to talk to managers, producers, and collaborators directly inside the app.",[962,2614,2616],{"id":2615},"the-stack","The stack",[845,2618,2619,2625,2631],{},[848,2620,2621,2624],{},[883,2622,2623],{},"Backend:"," Django with Django REST Framework",[848,2626,2627,2630],{},[883,2628,2629],{},"Frontend:"," Nuxt 4 (Vue 3) with SSR",[848,2632,2633,2636],{},[883,2634,2635],{},"Chat provider:"," Stream Chat",[962,2638,2640],{"id":2639},"the-integration","The integration",[729,2642,2643,2644,2647],{},"The entire chat feature was shipped in ",[883,2645,2646],{},"one sprint"," (two weeks). Here's how it works:",[729,2649,2650],{},[883,2651,2652],{},"Backend (3 endpoints, ~100 lines of code):",[729,2654,2655],{},"The Django backend handles authentication and channel management. Stream never sees your users' passwords. The backend generates short-lived tokens and manages channel creation.",[900,2657,2660],{"className":1257,"code":2658,"filename":2659,"language":1139,"meta":906,"style":906},"class TokenView(APIView):\n    \"\"\"Generate a Stream Chat token for the authenticated user.\"\"\"\n\n    def post(self, request):\n        client = StreamChat(\n            api_key=settings.STREAM_API_KEY,\n            api_secret=settings.STREAM_API_SECRET,\n        )\n        # Upsert user info to Stream\n        client.upsert_user({\"id\": str(request.user.id), \"name\": request.user.name})\n        token = client.create_token(str(request.user.id))\n        return Response({\"token\": token, \"api_key\": settings.STREAM_API_KEY})\n","views.py",[782,2661,2662,2678,2687,2692,2713,2726,2744,2760,2765,2770,2832,2866],{"__ignoreMap":906},[974,2663,2664,2667,2670,2672,2675],{"class":976,"line":977},[974,2665,2666],{"class":1044},"class",[974,2668,2669],{"class":980}," TokenView",[974,2671,1287],{"class":1035},[974,2673,2674],{"class":980},"APIView",[974,2676,2677],{"class":1035},"):\n",[974,2679,2680,2682,2685],{"class":976,"line":991},[974,2681,1313],{"class":1312},[974,2683,2684],{"class":1316},"Generate a Stream Chat token for the authenticated user.",[974,2686,1320],{"class":1312},[974,2688,2689],{"class":976,"line":1001},[974,2690,2691],{"emptyLinePlaceholder":1607},"\n",[974,2693,2694,2697,2700,2702,2706,2708,2711],{"class":976,"line":1070},[974,2695,2696],{"class":1044},"    def",[974,2698,2699],{"class":994}," post",[974,2701,1287],{"class":1035},[974,2703,2705],{"class":2704},"s5tWE","self",[974,2707,1116],{"class":1035},[974,2709,2710],{"class":1290}," request",[974,2712,2677],{"class":1035},[974,2714,2715,2718,2720,2723],{"class":976,"line":1094},[974,2716,2717],{"class":1325},"        client ",[974,2719,1329],{"class":1035},[974,2721,2722],{"class":994}," StreamChat",[974,2724,2725],{"class":1035},"(\n",[974,2727,2728,2731,2733,2736,2738,2742],{"class":976,"line":1165},[974,2729,2730],{"class":1290},"            api_key",[974,2732,1329],{"class":1035},[974,2734,2735],{"class":994},"settings",[974,2737,748],{"class":1035},[974,2739,2741],{"class":2740},"swJcz","STREAM_API_KEY",[974,2743,1091],{"class":1035},[974,2745,2746,2749,2751,2753,2755,2758],{"class":976,"line":1171},[974,2747,2748],{"class":1290},"            api_secret",[974,2750,1329],{"class":1035},[974,2752,2735],{"class":994},[974,2754,748],{"class":1035},[974,2756,2757],{"class":2740},"STREAM_API_SECRET",[974,2759,1091],{"class":1035},[974,2761,2762],{"class":976,"line":1177},[974,2763,2764],{"class":1035},"        )\n",[974,2766,2767],{"class":976,"line":2176},[974,2768,2769],{"class":1316},"        # Upsert user info to Stream\n",[974,2771,2772,2775,2777,2780,2783,2785,2788,2790,2792,2794,2796,2799,2801,2804,2806,2808,2811,2813,2816,2818,2820,2822,2824,2826,2828,2830],{"class":976,"line":2197},[974,2773,2774],{"class":1325},"        client",[974,2776,748],{"class":1035},[974,2778,2779],{"class":994},"upsert_user",[974,2781,2782],{"class":1035},"({",[974,2784,1048],{"class":1035},[974,2786,2787],{"class":984},"id",[974,2789,1048],{"class":1035},[974,2791,1022],{"class":1035},[974,2793,1296],{"class":980},[974,2795,1287],{"class":1035},[974,2797,2798],{"class":994},"request",[974,2800,748],{"class":1035},[974,2802,2803],{"class":2740},"user",[974,2805,748],{"class":1035},[974,2807,2787],{"class":2740},[974,2809,2810],{"class":1035},"),",[974,2812,1084],{"class":1035},[974,2814,2815],{"class":984},"name",[974,2817,1048],{"class":1035},[974,2819,1022],{"class":1035},[974,2821,2710],{"class":994},[974,2823,748],{"class":1035},[974,2825,2803],{"class":2740},[974,2827,748],{"class":1035},[974,2829,2815],{"class":2740},[974,2831,1456],{"class":1035},[974,2833,2834,2837,2839,2841,2843,2846,2848,2851,2853,2855,2857,2859,2861,2863],{"class":976,"line":2217},[974,2835,2836],{"class":1325},"        token ",[974,2838,1329],{"class":1035},[974,2840,1332],{"class":1325},[974,2842,748],{"class":1035},[974,2844,2845],{"class":994},"create_token",[974,2847,1287],{"class":1035},[974,2849,2850],{"class":980},"str",[974,2852,1287],{"class":1035},[974,2854,2798],{"class":994},[974,2856,748],{"class":1035},[974,2858,2803],{"class":2740},[974,2860,748],{"class":1035},[974,2862,2787],{"class":2740},[974,2864,2865],{"class":1035},"))\n",[974,2867,2868,2871,2874,2876,2878,2881,2883,2885,2888,2890,2892,2895,2897,2899,2902,2904,2906],{"class":976,"line":1608},[974,2869,2870],{"class":1312},"        return",[974,2872,2873],{"class":994}," Response",[974,2875,2782],{"class":1035},[974,2877,1048],{"class":1035},[974,2879,2880],{"class":984},"token",[974,2882,1048],{"class":1035},[974,2884,1022],{"class":1035},[974,2886,2887],{"class":994}," token",[974,2889,1116],{"class":1035},[974,2891,1084],{"class":1035},[974,2893,2894],{"class":984},"api_key",[974,2896,1048],{"class":1035},[974,2898,1022],{"class":1035},[974,2900,2901],{"class":994}," settings",[974,2903,748],{"class":1035},[974,2905,2741],{"class":2740},[974,2907,1456],{"class":1035},[900,2909,2911],{"className":1257,"code":2910,"filename":2659,"language":1139,"meta":906,"style":906},"class CreateChannelView(APIView):\n    \"\"\"Create or get a messaging channel between two users.\"\"\"\n\n    def post(self, request):\n        other_user_id = request.data[\"user_id\"]\n        # Deterministic channel ID from sorted user IDs\n        members = sorted([str(request.user.id), str(other_user_id)])\n        channel_id = f\"chat-{members[0]}-{members[1]}\"\n\n        client = StreamChat(\n            api_key=settings.STREAM_API_KEY,\n            api_secret=settings.STREAM_API_SECRET,\n        )\n        channel = client.channel(\"messaging\", channel_id, {\"members\": members})\n        channel.create(str(request.user.id))\n        return Response({\"channel_id\": channel_id})\n",[782,2912,2913,2926,2935,2939,2955,2981,2986,3025,3073,3077,3087,3101,3115,3119,3166,3195],{"__ignoreMap":906},[974,2914,2915,2917,2920,2922,2924],{"class":976,"line":977},[974,2916,2666],{"class":1044},[974,2918,2919],{"class":980}," CreateChannelView",[974,2921,1287],{"class":1035},[974,2923,2674],{"class":980},[974,2925,2677],{"class":1035},[974,2927,2928,2930,2933],{"class":976,"line":991},[974,2929,1313],{"class":1312},[974,2931,2932],{"class":1316},"Create or get a messaging channel between two users.",[974,2934,1320],{"class":1312},[974,2936,2937],{"class":976,"line":1001},[974,2938,2691],{"emptyLinePlaceholder":1607},[974,2940,2941,2943,2945,2947,2949,2951,2953],{"class":976,"line":1070},[974,2942,2696],{"class":1044},[974,2944,2699],{"class":994},[974,2946,1287],{"class":1035},[974,2948,2705],{"class":2704},[974,2950,1116],{"class":1035},[974,2952,2710],{"class":1290},[974,2954,2677],{"class":1035},[974,2956,2957,2960,2962,2964,2966,2969,2972,2974,2977,2979],{"class":976,"line":1094},[974,2958,2959],{"class":1325},"        other_user_id ",[974,2961,1329],{"class":1035},[974,2963,2710],{"class":1325},[974,2965,748],{"class":1035},[974,2967,2968],{"class":2740},"data",[974,2970,2971],{"class":1035},"[",[974,2973,1048],{"class":1035},[974,2975,2976],{"class":984},"user_id",[974,2978,1048],{"class":1035},[974,2980,1162],{"class":1035},[974,2982,2983],{"class":976,"line":1165},[974,2984,2985],{"class":1316},"        # Deterministic channel ID from sorted user IDs\n",[974,2987,2988,2991,2993,2996,2999,3001,3003,3005,3007,3009,3011,3013,3015,3017,3019,3022],{"class":976,"line":1171},[974,2989,2990],{"class":1325},"        members ",[974,2992,1329],{"class":1035},[974,2994,2995],{"class":994}," sorted",[974,2997,2998],{"class":1035},"([",[974,3000,2850],{"class":980},[974,3002,1287],{"class":1035},[974,3004,2798],{"class":994},[974,3006,748],{"class":1035},[974,3008,2803],{"class":2740},[974,3010,748],{"class":1035},[974,3012,2787],{"class":2740},[974,3014,2810],{"class":1035},[974,3016,1296],{"class":980},[974,3018,1287],{"class":1035},[974,3020,3021],{"class":994},"other_user_id",[974,3023,3024],{"class":1035},")])\n",[974,3026,3027,3030,3032,3035,3038,3041,3044,3046,3049,3052,3055,3058,3060,3062,3064,3067,3069,3071],{"class":976,"line":1177},[974,3028,3029],{"class":1325},"        channel_id ",[974,3031,1329],{"class":1035},[974,3033,3034],{"class":1044}," f",[974,3036,3037],{"class":984},"\"chat-",[974,3039,3040],{"class":1076},"{",[974,3042,3043],{"class":1325},"members",[974,3045,2971],{"class":1035},[974,3047,3048],{"class":1076},"0",[974,3050,3051],{"class":1035},"]",[974,3053,3054],{"class":1076},"}",[974,3056,3057],{"class":984},"-",[974,3059,3040],{"class":1076},[974,3061,3043],{"class":1325},[974,3063,2971],{"class":1035},[974,3065,3066],{"class":1076},"1",[974,3068,3051],{"class":1035},[974,3070,3054],{"class":1076},[974,3072,2214],{"class":984},[974,3074,3075],{"class":976,"line":2176},[974,3076,2691],{"emptyLinePlaceholder":1607},[974,3078,3079,3081,3083,3085],{"class":976,"line":2197},[974,3080,2717],{"class":1325},[974,3082,1329],{"class":1035},[974,3084,2722],{"class":994},[974,3086,2725],{"class":1035},[974,3088,3089,3091,3093,3095,3097,3099],{"class":976,"line":2217},[974,3090,2730],{"class":1290},[974,3092,1329],{"class":1035},[974,3094,2735],{"class":994},[974,3096,748],{"class":1035},[974,3098,2741],{"class":2740},[974,3100,1091],{"class":1035},[974,3102,3103,3105,3107,3109,3111,3113],{"class":976,"line":1608},[974,3104,2748],{"class":1290},[974,3106,1329],{"class":1035},[974,3108,2735],{"class":994},[974,3110,748],{"class":1035},[974,3112,2757],{"class":2740},[974,3114,1091],{"class":1035},[974,3116,3117],{"class":976,"line":2226},[974,3118,2764],{"class":1035},[974,3120,3122,3125,3127,3129,3131,3134,3136,3138,3141,3143,3145,3148,3150,3153,3155,3157,3159,3161,3164],{"class":976,"line":3121},14,[974,3123,3124],{"class":1325},"        channel ",[974,3126,1329],{"class":1035},[974,3128,1332],{"class":1325},[974,3130,748],{"class":1035},[974,3132,3133],{"class":994},"channel",[974,3135,1287],{"class":1035},[974,3137,1048],{"class":1035},[974,3139,3140],{"class":984},"messaging",[974,3142,1048],{"class":1035},[974,3144,1116],{"class":1035},[974,3146,3147],{"class":994}," channel_id",[974,3149,1116],{"class":1035},[974,3151,3152],{"class":1035}," {",[974,3154,1048],{"class":1035},[974,3156,3043],{"class":984},[974,3158,1048],{"class":1035},[974,3160,1022],{"class":1035},[974,3162,3163],{"class":994}," members",[974,3165,1456],{"class":1035},[974,3167,3169,3172,3174,3177,3179,3181,3183,3185,3187,3189,3191,3193],{"class":976,"line":3168},15,[974,3170,3171],{"class":1325},"        channel",[974,3173,748],{"class":1035},[974,3175,3176],{"class":994},"create",[974,3178,1287],{"class":1035},[974,3180,2850],{"class":980},[974,3182,1287],{"class":1035},[974,3184,2798],{"class":994},[974,3186,748],{"class":1035},[974,3188,2803],{"class":2740},[974,3190,748],{"class":1035},[974,3192,2787],{"class":2740},[974,3194,2865],{"class":1035},[974,3196,3198,3200,3202,3204,3206,3209,3211,3213,3215],{"class":976,"line":3197},16,[974,3199,2870],{"class":1312},[974,3201,2873],{"class":994},[974,3203,2782],{"class":1035},[974,3205,1048],{"class":1035},[974,3207,3208],{"class":984},"channel_id",[974,3210,1048],{"class":1035},[974,3212,1022],{"class":1035},[974,3214,3147],{"class":994},[974,3216,1456],{"class":1035},[920,3218,3219],{},[729,3220,3221,3222,3225],{},"The deterministic channel ID pattern (",[782,3223,3224],{},"chat-{user1}-{user2}"," with sorted IDs) means you always get the same channel for the same pair of users, no matter who initiates the conversation.",[729,3227,3228],{},[883,3229,3230],{},"Frontend (Vue composable + component):",[729,3232,3233],{},"The frontend connects to Stream using the token from the backend. All real-time updates, message rendering, and state management happen through Stream's JavaScript SDK.",[900,3235,3240],{"className":3236,"code":3237,"filename":3238,"language":3239,"meta":906,"style":906},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export async function inboxGetToken(): Promise\u003C{ token: string; api_key: string }> {\n  return await $api(endpoints.token, { method: \"POST\" })\n}\n\nexport async function inboxCreateChannel(userId: string): Promise\u003C{ channel_id: string }> {\n  return await $api(endpoints.channel, {\n    method: \"POST\",\n    body: { user_id: userId },\n  })\n}\n","inbox.service.ts","typescript",[782,3241,3242,3287,3328,3332,3336,3373,3393,3408,3428,3435],{"__ignoreMap":906},[974,3243,3244,3247,3250,3253,3256,3259,3262,3265,3267,3269,3272,3275,3278,3280,3282,3285],{"class":976,"line":977},[974,3245,3246],{"class":1312},"export",[974,3248,3249],{"class":1044}," async",[974,3251,3252],{"class":1044}," function",[974,3254,3255],{"class":994}," inboxGetToken",[974,3257,3258],{"class":1035},"():",[974,3260,3261],{"class":980}," Promise",[974,3263,3264],{"class":1035},"\u003C{",[974,3266,2887],{"class":2740},[974,3268,1022],{"class":1035},[974,3270,3271],{"class":980}," string",[974,3273,3274],{"class":1035},";",[974,3276,3277],{"class":2740}," api_key",[974,3279,1022],{"class":1035},[974,3281,3271],{"class":980},[974,3283,3284],{"class":1035}," }>",[974,3286,1053],{"class":1035},[974,3288,3289,3292,3295,3298,3300,3303,3305,3307,3309,3311,3314,3316,3318,3321,3323,3326],{"class":976,"line":991},[974,3290,3291],{"class":1312},"  return",[974,3293,3294],{"class":1312}," await",[974,3296,3297],{"class":994}," $api",[974,3299,1287],{"class":2740},[974,3301,3302],{"class":1325},"endpoints",[974,3304,748],{"class":1035},[974,3306,2880],{"class":1325},[974,3308,1116],{"class":1035},[974,3310,3152],{"class":1035},[974,3312,3313],{"class":2740}," method",[974,3315,1022],{"class":1035},[974,3317,1084],{"class":1035},[974,3319,3320],{"class":984},"POST",[974,3322,1048],{"class":1035},[974,3324,3325],{"class":1035}," }",[974,3327,1344],{"class":2740},[974,3329,3330],{"class":976,"line":1001},[974,3331,1180],{"class":1035},[974,3333,3334],{"class":976,"line":1070},[974,3335,2691],{"emptyLinePlaceholder":1607},[974,3337,3338,3340,3342,3344,3347,3349,3352,3354,3356,3359,3361,3363,3365,3367,3369,3371],{"class":976,"line":1094},[974,3339,3246],{"class":1312},[974,3341,3249],{"class":1044},[974,3343,3252],{"class":1044},[974,3345,3346],{"class":994}," inboxCreateChannel",[974,3348,1287],{"class":1035},[974,3350,3351],{"class":1290},"userId",[974,3353,1022],{"class":1035},[974,3355,3271],{"class":980},[974,3357,3358],{"class":1035},"):",[974,3360,3261],{"class":980},[974,3362,3264],{"class":1035},[974,3364,3147],{"class":2740},[974,3366,1022],{"class":1035},[974,3368,3271],{"class":980},[974,3370,3284],{"class":1035},[974,3372,1053],{"class":1035},[974,3374,3375,3377,3379,3381,3383,3385,3387,3389,3391],{"class":976,"line":1165},[974,3376,3291],{"class":1312},[974,3378,3294],{"class":1312},[974,3380,3297],{"class":994},[974,3382,1287],{"class":2740},[974,3384,3302],{"class":1325},[974,3386,748],{"class":1035},[974,3388,3133],{"class":1325},[974,3390,1116],{"class":1035},[974,3392,1053],{"class":1035},[974,3394,3395,3398,3400,3402,3404,3406],{"class":976,"line":1171},[974,3396,3397],{"class":2740},"    method",[974,3399,1022],{"class":1035},[974,3401,1084],{"class":1035},[974,3403,3320],{"class":984},[974,3405,1048],{"class":1035},[974,3407,1091],{"class":1035},[974,3409,3410,3413,3415,3417,3420,3422,3425],{"class":976,"line":1177},[974,3411,3412],{"class":2740},"    body",[974,3414,1022],{"class":1035},[974,3416,3152],{"class":1035},[974,3418,3419],{"class":2740}," user_id",[974,3421,1022],{"class":1035},[974,3423,3424],{"class":1325}," userId",[974,3426,3427],{"class":1035}," },\n",[974,3429,3430,3433],{"class":976,"line":2176},[974,3431,3432],{"class":1035},"  }",[974,3434,1344],{"class":2740},[974,3436,3437],{"class":976,"line":2197},[974,3438,1180],{"class":1035},[729,3440,3441,3442,1478,3445,3448],{},"The chat component listens for ",[782,3443,3444],{},"message.new",[782,3446,3447],{},"message.read"," events from Stream, updates the UI in real time, and tracks unread counts per conversation.",[729,3450,3451,3454],{},[883,3452,3453],{},"No database models needed."," Stream stores all messages, handles delivery, and manages state. The Django backend has zero chat-related database tables.",[962,3456,3458],{"id":3457},"what-we-shipped-in-one-sprint","What we shipped in one sprint",[933,3460,3462,3467,3472,3477,3482,3487],{"className":3461},[936,937,938,939,940],[942,3463],{"description":3464,"icon":3465,"title":3466},"1-to-1 conversations between any two users on the platform.","i-lucide-message-square","Direct Messaging",[942,3468],{"description":3469,"icon":3470,"title":3471},"Message delivery and read status, updated in real time.","i-lucide-check-check","Read Receipts",[942,3473],{"description":3474,"icon":3475,"title":3476},"Badge counts per conversation and a global unread indicator.","i-lucide-bell-dot","Unread Counts",[942,3478],{"description":3479,"icon":3480,"title":3481},"List of all available users with online/offline status.","i-lucide-users","User Roster",[942,3483],{"description":3484,"icon":3485,"title":3486},"Arrow keys to switch between conversations for power users.","i-lucide-keyboard","Keyboard Navigation",[942,3488],{"description":3489,"icon":3490,"title":3491},"Split-panel on desktop, slideover on mobile. Same functionality everywhere.","i-lucide-smartphone","Responsive UI",[750,3493,3495],{"id":3494},"when-does-this-approach-make-sense","When does this approach make sense?",[729,3497,3498],{},"This build-vs-buy decision is clear-cut in most cases:",[729,3500,3501],{},[883,3502,3503],{},"Use a managed service when:",[845,3505,3506,3509,3512,3515],{},[848,3507,3508],{},"Chat is a feature in your app, not the core product",[848,3510,3511],{},"You need it reliable and production-ready from day one",[848,3513,3514],{},"Your engineering budget should go toward what makes your product unique",[848,3516,3517],{},"You expect to scale without rebuilding infrastructure",[729,3519,3520],{},[883,3521,3522],{},"Consider building custom when:",[845,3524,3525,3528,3531],{},[848,3526,3527],{},"Messaging IS your core product (you're building the next Slack)",[848,3529,3530],{},"You have very specific compliance requirements that no provider can meet",[848,3532,3533],{},"You have a dedicated team to maintain it long-term",[729,3535,3536],{},"For most platforms we build at MusicTech Lab, the choice is obvious. Our clients' budgets should go toward the features that set their product apart, not toward reinventing infrastructure that already exists.",[750,3538,3540],{"id":3539},"key-takeaways","Key Takeaways",[933,3542,3544,3549,3554,3559],{"className":3543},[936,937,2521,939,940],[942,3545],{"description":3546,"icon":3547,"title":3548},"Real-time messaging involves WebSockets, delivery guarantees, storage, push notifications, and scaling. It's a product, not a feature.","i-lucide-alert-triangle","Chat Is Not a Simple Feature",[942,3550],{"description":3551,"icon":3552,"title":3553},"Services like Stream give you years of engineering through a single API. Your budget should go toward what makes your product unique.","i-lucide-shopping-bag","Buy, Don't Build",[942,3555],{"description":3556,"icon":3557,"title":3558},"We shipped direct messaging with read receipts, unread counts, and responsive UI in two weeks using Stream Chat + Django + Nuxt.","i-lucide-zap","One Sprint Integration",[942,3560],{"description":3561,"icon":2534,"title":3562},"No chat-related tables in your database. Stream handles all message storage, delivery, and state management.","Zero Database Overhead",[750,3564,3566],{"id":3565},"the-bottom-line","The bottom line",[729,3568,3569],{},"Chat is one of those features where the gap between \"looks simple\" and \"is simple\" is enormous. A managed service like Stream closes that gap. You get a production-grade messaging system integrated in days, not months.",[729,3571,3572],{},"We've used this approach across multiple projects, and it works every time. The client gets real-time messaging that just works. We get to spend our time on the features that actually matter for their business.",[3574,3575],"hr",{},[729,3577,3578],{},[851,3579,3580,3581,3586],{},"Planning an app with real-time features like chat, notifications, or live updates? ",[733,3582,3585],{"href":3583,"rel":3584},"https://www.musictechlab.io/contact",[737],"Let's talk"," about the right building blocks for your project.",[1569,3588,3589],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s5tWE, html code.shiki .s5tWE{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#F07178;--shiki-default-font-style:italic;--shiki-dark:#F07178;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":906,"searchDepth":991,"depth":991,"links":3591},[3592,3593,3594,3595,3600,3601,3602],{"id":2503,"depth":991,"text":2504},{"id":2513,"depth":991,"text":2514},{"id":2561,"depth":991,"text":2562},{"id":2603,"depth":991,"text":2604,"children":3596},[3597,3598,3599],{"id":2615,"depth":1001,"text":2616},{"id":2639,"depth":1001,"text":2640},{"id":3457,"depth":1001,"text":3458},{"id":3494,"depth":991,"text":3495},{"id":3539,"depth":991,"text":3540},{"id":3565,"depth":991,"text":3566},"2026-04-02T00:00:00.000Z","In-app messaging sounds like a simple feature, but building it from scratch is a project on its own. Here's why we use managed chat services and how we integrated one in a single sprint.",[3606,3609,3612],{"question":3607,"answer":3608},"How long does it take to add chat to an existing app?","Using a managed service like Stream Chat, a working integration with direct messaging, read receipts, and unread counts can be done in a single two-week sprint.",{"question":3610,"answer":3611},"Why not build chat from scratch?","Real-time messaging involves WebSocket infrastructure, message ordering, delivery guarantees, offline handling, and scaling. It's a standalone product, not a feature. Managed services give you years of engineering through a single API.",{"question":3613,"answer":3614},"What is Stream Chat?","Stream Chat (getstream.io) is a managed messaging API that provides real-time chat infrastructure. You integrate it into your app via SDKs, and Stream handles the hard parts: delivery, storage, real-time sync, and scaling.",{"src":3616},"/images/blog/why-we-dont-build-chat-from-scratch.webp",{},{"title":3619,"description":3620},"Why We Don't Build Chat From Scratch | MusicTech Lab","How we added real-time messaging to a Django + Nuxt app using Stream Chat in one sprint. Build vs buy for in-app chat.",[3622,3623,3624,3625,3626,3627],"stream-chat","real-time","django","nuxt","integration","case-study","Bl7ZxAPBhJZuRu_kxuB2LMG4hUkC76QCIaoV-sS_Czo",1777554173202]