DEV Community

VoiceDeveloper
VoiceDeveloper

Posted on

Integrating AI Voice with Notion and Productivity Tools

Why Add AI Voice to Your Notion Workflow?

If you’ve ever found yourself juggling meetings, brainstorming sessions, and endless to‑do lists, you know how valuable “hands‑free” productivity can be. Voice‑first interfaces let you capture ideas, generate summaries, or even read back your notes without breaking your flow.

Combining text‑to‑speech (TTS) and voice cloning with Notion’s flexible database API opens up a whole new class of productivity hacks:

  • Instant audio summaries of meeting notes that you can listen to on the go.
  • Voice‑driven content creation—speak, have it transcribed, then let an AI voice read it back for polishing.
  • Personalized audio reminders that sound like you (or your favorite narrator) and appear right inside Notion pages.

In this post I’ll walk you through a practical setup using the ElevenLabs API (my go‑to service for natural‑sounding speech), a little Python, and Notion’s REST API. By the end you’ll have a script that takes any Notion page, generates a high‑quality audio file, and uploads it back as an attachment—ready for your next commute.


Getting Started with ElevenLabs

ElevenLabs offers a robust TTS endpoint that supports voice cloning, SSML, and streaming output. Sign up with the affiliate link below to unlock a generous free tier and instantly get an API key.

👉 Try ElevenLabs now

Once you have your key, keep it safe—treat it like any other secret token.


Prerequisites

Tool Version
Python 3.9+
requests library latest (pip install requests)
Notion integration token created in Notion → Settings & Members → Integrations
Notion database/page ID copy from the URL (https://www.notion.so/yourworkspace/Page-Name-<ID>)

Step 1: Generate Audio with ElevenLabs (Python)

Below is a minimal function that sends plain text to ElevenLabs and saves the resulting MP3.

import requests

ELEVEN_API_KEY = "YOUR_ELEVENLABS_API_KEY"
VOICE_ID = "21m00Tcm4TlvDq8ikWAM"   # default "Rachel" voice; replace with your cloned voice ID

def text_to_speech(text: str, output_path: str) -> None:
    url = f"https://api.elevenlabs.io/v1/text-to-speech/{VOICE_ID}"
    headers = {
        "xi-api-key": ELEVEN_API_KEY,
        "Content-Type": "application/json"
    }
    payload = {
        "text": text,
        "voice_settings": {
            "stability": 0.75,
            "similarity_boost": 0.85
        }
    }

    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()

    with open(output_path, "wb") as f:
        f.write(response.content)

# Example usage
if __name__ == "__main__":
    sample = "Hey team, here’s a quick recap of today’s sprint planning."
    text_to_speech(sample, "sprint_summary.mp3")
Enter fullscreen mode Exit fullscreen mode

Key points

  • VOICE_ID can be any voice you’ve created in the ElevenLabs dashboard, including cloned voices that sound like you.
  • Adjust stability and similarity_boost to fine‑tune the prosody.
  • The endpoint returns a binary MP3 payload, which we write straight to disk.

If you prefer a quick curl test, here’s the equivalent:

curl -X POST "https://api.elevenlabs.io/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM" \
  -H "xi-api-key: YOUR_ELEVENLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "text": "Your text goes here.",
        "voice_settings": { "stability": 0.75, "similarity_boost": 0.85 }
      }' \
  --output output.mp3
Enter fullscreen mode Exit fullscreen mode

Step 2: Pull Content from Notion

Let’s fetch the plain‑text content of a Notion page. The Notion API returns a block‑tree; we’ll flatten it into a simple string.

import os
import json
import requests

NOTION_TOKEN = "YOUR_NOTION_INTEGRATION_TOKEN"
PAGE_ID = "YOUR_NOTION_PAGE_ID"

def get_page_text(page_id: str) -> str:
    url = f"https://api.notion.com/v1/blocks/{page_id}/children"
    headers = {
        "Authorization": f"Bearer {NOTION_TOKEN}",
        "Notion-Version": "2022-06-28"
    }

    response = requests.get(url, headers=headers)
    response.raise_for_status()
    data = response.json()

    # Simple flattening of paragraph and heading blocks
    texts = []
    for block in data.get("results", []):
        if block["type"] in ("paragraph", "heading_1", "heading_2", "heading_3"):
            rich_text = block[block["type"]].get("rich_text", [])
            plain = "".join(rt["plain_text"] for rt in rich_text)
            texts.append(plain)

    return "\n".join(texts)

# Example
if __name__ == "__main__":
    page_text = get_page_text(PAGE_ID)
    print(page_text[:200])  # preview
Enter fullscreen mode Exit fullscreen mode

Tip: For larger pages you’ll need to paginate using the next_cursor field. The same pattern applies—just loop until has_more is False.


Step 3: Attach the Audio Back to Notion

Now that we have sprint_summary.mp3, we’ll upload it as a file block.

def upload_file_to_notion(page_id: str, file_path: str) -> None:
    # Step 1: Get a temporary upload URL from Notion
    upload_url = "https://api.notion.com/v1/files"
    headers = {
        "Authorization": f"Bearer {NOTION_TOKEN}",
        "Notion-Version": "2022-06-28",
        "Content-Type": "application/json"
    }
    payload = {
        "type": "external",
        "external": {"url": f"https://www.notion.so/file/{os.path.basename(file_path)}"}
    }

    # Notion actually expects you to host the file elsewhere.
    # A simple workaround: use a public S3 bucket or a service like https://file.io
    # For brevity, let’s assume the file is already publicly reachable at `public_url`.
    public_url = "YOUR_PUBLIC_AUDIO_URL"

    # Step 2: Create a file block on the page
    block_url = f"https://api.notion.com/v1/blocks/{page_id}/children"
    block_payload = {
        "children": [
            {
                "object": "block",
                "type": "file",
                "file": {
                    "type": "external",
                    "external": {"url": public_url}
                }
            }
        ]
    }

    resp = requests.patch(block_url, headers=headers, json=block_payload)
    resp.raise_for_status()
    print("Audio attached to Notion page!")

# Usage (after you’ve uploaded the MP3 somewhere public)
upload_file_to_notion(PAGE_ID, "sprint_summary.mp3")
Enter fullscreen mode Exit fullscreen mode

Why the extra step? Notion doesn’t host arbitrary binary uploads; you need a publicly reachable URL. Services like Amazon S3, Google Cloud Storage, or even a quick curl -T to file.io work fine for prototypes.


Putting It All Together

Below is a compact script that ties the three steps into a single command:

def main():
    # 1️⃣ Pull page text
    text = get_page_text(PAGE_ID)
    if not text:
        print("No readable content found.")
        return

    # 2️⃣ Generate audio
    audio_path = "page_audio.mp3"
    text_to_speech(text, audio_path)
    print(f"Audio saved to {audio_path}")

    # 3️⃣ Upload audio somewhere public (example using file.io)
    with open(audio_path, "rb") as f:
        upload_resp = requests.post(
            "https://file.io",
            files={"file": f}
        )
    upload_resp.raise_for_status()
    public_url = upload_resp.json()["link"]
    print(f"Public URL: {public_url}")

    # 4️⃣ Attach to Notion
    # Re‑use the block‑creation snippet but replace `public_url`
    block_url = f"https://api.notion.com/v1/blocks/{PAGE_ID}/children"
    headers = {
        "Authorization": f"Bearer {NOTION_TOKEN}",
        "Notion-Version": "2022-06-28",
        "Content-Type": "application/json"
    }
    block_payload = {
        "children": [
            {
                "object": "block",
                "type": "file",
                "file": {
                    "type": "external",
                    "external": {"url": public_url}
                }
            }
        ]
    }
    resp = requests.patch(block_url, headers=headers, json=block_payload)
    resp.raise_for_status()
    print("✅ Audio attached to Notion!")

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Run python notion_voice.py and watch your Notion page gain a fresh audio summary—perfect for listening during a walk or while driving.


Extending the Idea

  • Voice Cloning: Upload a short sample of your own voice to ElevenLabs, get a voice_id, and replace the default ID in text_to_speech. Suddenly your notes sound like you speaking!
  • Automation Platforms: Hook the script into Zapier, Make (Integromat), or a scheduled GitHub Action to generate audio nightly for a “Daily Digest” page.
  • Interactive Bots: Pair the TTS output with a speech‑to‑text service (e.g., Whisper) to build a bi‑directional voice assistant that reads from and writes to Notion.

Wrapping Up

Integrating AI voice into Notion doesn’t require a massive infrastructure—just a few API calls, a bit of Python, and a reliable TTS provider. By leveraging ElevenLabs, you get crystal‑clear, customizable speech that can be cloned, fine‑tuned, and deployed instantly.

Ready to give your Notion workspace a voice? Grab your ElevenLabs API key, try the snippets above, and start turning static pages into dynamic audio assets.

👉 Try ElevenLabs today and bring your notes to life: https://try.elevenlabs.io/kr07zfuqn1bp

Top comments (0)