Repository: supabase/supabase
Pull request #33989: Thor/add elevenlabs streaming example n docs
Author: login: thorwebdev
Created: 2025-03-04T09:07:18Z | Merged: 2025-03-07T08:08:56Z

Add two ElevenLabs examples and docs as discussed with @laktek 


Diff:
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 6b16ae1c00b51..01d8fb4f702f4 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -1529,6 +1529,14 @@ export const functions: NavMenuConstant = {
           name: 'Type-Safe SQL with Kysely',
           url: '/guides/functions/kysely-postgres',
         },
+        {
+          name: 'Text To Speech with ElevenLabs',
+          url: '/guides/functions/examples/elevenlabs-generate-speech-stream',
+        },
+        {
+          name: 'Speech Transcription with ElevenLabs',
+          url: '/guides/functions/examples/elevenlabs-transcribe-speech',
+        },
       ],
     },
   ],
diff --git a/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx b/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx
new file mode 100644
index 0000000000000..c471b1a7112b3
--- /dev/null
+++ b/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx
@@ -0,0 +1,245 @@
+---
+title: Streaming Speech with ElevenLabs
+subtitle: Generate and stream speech through Supabase Edge Functions. Store speech in Supabase Storage and cache responses via built-in CDN.
+tocVideo: '4Roog4PAmZ8'
+---
+
+## Introduction
+
+In this tutorial you will learn how to build and edge API to generate, stream, store, and cache speech from text using Supabase Edge Functions, Supabase Storage, and ElevenLabs.`
+
+<Admonition type="tip">
+  Find the [example project on
+  GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/text-to-speech/supabase/stream-and-cache-storage).
+</Admonition>
+
+## Requirements
+
+- An ElevenLabs account with an [API key](/app/settings/api-keys).
+- A [Supabase](https://supabase.com) account (you can sign up for a free account via [database.new](https://database.new)).
+- The [Supabase CLI](https://supabase.com/docs/guides/local-development) installed on your machine.
+- The [Deno runtime](https://docs.deno.com/runtime/getting_started/installation/) installed on your machine and optionally [setup in your favourite IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
+
+## Setup
+
+### Create a Supabase project locally
+
+After installing the [Supabase CLI](https://supabase.com/docs/guides/local-development), run the following command to create a new Supabase project locally:
+
+```bash
+supabase init
+```
+
+### Configure the storage bucket
+
+You can configure the Supabase CLI to automatically generate a storage bucket by adding this configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[storage.buckets.audio]
+public = false
+file_size_limit = "50MiB"
+allowed_mime_types = ["audio/mp3"]
+objects_path = "./audio"
+```
+
+<Admonition type="tip">
+  Upon running `supabase start` this will create a new storage bucket in your local Supabase
+  project. Should you want to push this to your hosted Supabase project, you can run `supabase seed
+  buckets --linked`.
+</Admonition>
+
+### Configure background tasks for Supabase Edge Functions
+
+To use background tasks in Supabase Edge Functions when developing locally, you need to add the following configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[edge_runtime]
+policy = "per_worker"
+```
+
+<Admonition type="tip">
+  When running with `per_worker` policy, Function won't auto-reload on edits. You will need to
+  manually restart it by running `supabase functions serve`.
+</Admonition>
+
+### Create a Supabase Edge Function for speech generation
+
+Create a new Edge Function by running the following command:
+
+```bash
+supabase functions new text-to-speech
+```
+
+If you're using VS Code or Cursor, select `y` when the CLI prompts "Generate VS Code settings for Deno? [y/N]"!
+
+### Set up the environment variables
+
+Within the `supabase/functions` directory, create a new `.env` file and add the following variables:
+
+```env supabase/functions/.env
+# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
+ELEVENLABS_API_KEY=your_api_key
+```
+
+### Dependencies
+
+The project uses a couple of dependencies:
+
+- The [@supabase/supabase-js](https://supabase.com/docs/reference/javascript) library to interact with the Supabase database.
+- The ElevenLabs [JavaScript SDK](/docs/quickstart) to interact with the text-to-speech API.
+- The open-source [object-hash](https://www.npmjs.com/package/object-hash) to generate a hash from the request parameters.
+
+Since Supabase Edge Function uses the [Deno runtime](https://deno.land/), you don't need to install the dependencies, rather you can [import](https://docs.deno.com/examples/npm/) them via the `npm:` prefix.
+
+## Code the Supabase Edge Function
+
+In your newly created `supabase/functions/text-to-speech/index.ts` file, add the following code:
+
+```ts supabase/functions/text-to-speech/index.ts
+// Setup type definitions for built-in Supabase Runtime APIs
+import 'jsr:@supabase/functions-js/edge-runtime.d.ts'
+import { createClient } from 'jsr:@supabase/supabase-js@2'
+import { ElevenLabsClient } from 'npm:elevenlabs@1.52.0'
+import * as hash from 'npm:object-hash'
+
+const supabase = createClient(
+  Deno.env.get('SUPABASE_URL')!,
+  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
+)
+
+const client = new ElevenLabsClient({
+  apiKey: Deno.env.get('ELEVENLABS_API_KEY'),
+})
+
+// Upload audio to Supabase Storage in a background task
+async function uploadAudioToStorage(stream: ReadableStream, requestHash: string) {
+  const { data, error } = await supabase.storage
+    .from('audio')
+    .upload(`${requestHash}.mp3`, stream, {
+      contentType: 'audio/mp3',
+    })
+
+  console.log('Storage upload result', { data, error })
+}
+
+Deno.serve(async (req) => {
+  // To secure your function for production, you can for example validate the request origin,
+  // or append a user access token and validate it with Supabase Auth.
+  console.log('Request origin', req.headers.get('host'))
+  const url = new URL(req.url)
+  const params = new URLSearchParams(url.search)
+  const text = params.get('text')
+  const voiceId = params.get('voiceId') ?? 'JBFqnCBsd6RMkjVDRZzb'
+
+  const requestHash = hash.MD5({ text, voiceId })
+  console.log('Request hash', requestHash)
+
+  // Check storage for existing audio file
+  const { data } = await supabase.storage.from('audio').createSignedUrl(`${requestHash}.mp3`, 60)
+
+  if (data) {
+    console.log('Audio file found in storage', data)
+    const storageRes = await fetch(data.signedUrl)
+    if (storageRes.ok) return storageRes
+  }
+
+  if (!text) {
+    return new Response(JSON.stringify({ error: 'Text parameter is required' }), {
+      status: 400,
+      headers: { 'Content-Type': 'application/json' },
+    })
+  }
+
+  try {
+    console.log('ElevenLabs API call')
+    const response = await client.textToSpeech.convertAsStream(voiceId, {
+      output_format: 'mp3_44100_128',
+      model_id: 'eleven_multilingual_v2',
+      text,
+    })
+
+    const stream = new ReadableStream({
+      async start(controller) {
+        for await (const chunk of response) {
+          controller.enqueue(chunk)
+        }
+        controller.close()
+      },
+    })
+
+    // Branch stream to Supabase Storage
+    const [browserStream, storageStream] = stream.tee()
+
+    // Upload to Supabase Storage in the background
+    EdgeRuntime.waitUntil(uploadAudioToStorage(storageStream, requestHash))
+
+    // Return the streaming response immediately
+    return new Response(browserStream, {
+      headers: {
+        'Content-Type': 'audio/mpeg',
+      },
+    })
+  } catch (error) {
+    console.log('error', { error })
+    return new Response(JSON.stringify({ error: error.message }), {
+      status: 500,
+      headers: { 'Content-Type': 'application/json' },
+    })
+  }
+})
+```
+
+## Run locally
+
+To run the function locally, run the following commands:
+
+```bash
+supabase start
+```
+
+Once the local Supabase stack is up and running, run the following command to start the function and observe the logs:
+
+```bash
+supabase functions serve
+```
+
+### Try it out
+
+Navigate to `http://127.0.0.1:54321/functions/v1/text-to-speech?text=hello%20world` to hear the function in action.
+
+Afterwards, navigate to `http://127.0.0.1:54323/project/default/storage/buckets/audio` to see the audio file in your local Supabase Storage bucket.
+
+## Deploy to Supabase
+
+If you haven't already, create a new Supabase account at [database.new](https://database.new) and link the local project to your Supabase account:
+
+```bash
+supabase link
+```
+
+Once done, run the following command to deploy the function:
+
+```bash
+supabase functions deploy
+```
+
+### Set the function secrets
+
+Now that you have all your secrets set locally, you can run the following command to set the secrets in your Supabase project:
+
+```bash
+supabase secrets set --env-file supabase/functions/.env
+```
+
+## Test the function
+
+The function is designed in a way that it can be used directly as a source for an `<audio>` element.
+
+```html
+<audio
+  src="https://${SUPABASE_PROJECT_REF}.supabase.co/functions/v1/text-to-speech?text=Hello%2C%20world!&voiceId=JBFqnCBsd6RMkjVDRZzb"
+  controls
+/>
+```
+
+You can find an example frontend implementation in the complete code example on [GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/text-to-speech/supabase/stream-and-cache-storage/src/pages/Index.tsx).
diff --git a/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx b/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx
new file mode 100644
index 0000000000000..a697da1c10403
--- /dev/null
+++ b/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx
@@ -0,0 +1,297 @@
+---
+title: Transcription Telegram Bot
+subtitle: Build a Telegram bot that transcribes audio and video messages in 99 languages using TypeScript with Deno in Supabase Edge Functions.
+tocVideo: 'CE4iPp7kd7Q'
+---
+
+## Introduction
+
+In this tutorial you will learn how to build a Telegram bot that transcribes audio and video messages in 99 languages using TypeScript and the ElevenLabs Scribe model via the speech-to-text API.
+
+To check out what the end result will look like, you can test out the [t.me/ElevenLabsScribeBot](https://t.me/ElevenLabsScribeBot)
+
+<Admonition type="tip">
+  Find the [example project on
+  GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/speech-to-text/telegram-transcription-bot).
+</Admonition>
+
+## Requirements
+
+- An ElevenLabs account with an [API key](/app/settings/api-keys).
+- A [Supabase](https://supabase.com) account (you can sign up for a free account via [database.new](https://database.new)).
+- The [Supabase CLI](https://supabase.com/docs/guides/local-development) installed on your machine.
+- The [Deno runtime](https://docs.deno.com/runtime/getting_started/installation/) installed on your machine and optionally [setup in your favourite IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
+- A [Telegram](https://telegram.org) account.
+
+## Setup
+
+### Register a Telegram bot
+
+Use the [BotFather](https://t.me/BotFather) to create a new Telegram bot. Run the `/newbot` command and follow the instructions to create a new bot. At the end, you will receive your secret bot token. Note it down securely for the next step.
+
+![BotFather](/docs/img/guides/functions/elevenlabs/bot-father.png)
+
+### Create a Supabase project locally
+
+After installing the [Supabase CLI](https://supabase.com/docs/guides/local-development), run the following command to create a new Supabase project locally:
+
+```bash
+supabase init
+```
+
+### Create a database table to log the transcription results
+
+Next, create a new database table to log the transcription results:
+
+```bash
+supabase migrations new init
+```
+
+This will create a new migration file in the `supabase/migrations` directory. Open the file and add the following SQL:
+
+```sql supabase/migrations/init.sql
+CREATE TABLE IF NOT EXISTS transcription_logs (
+  id BIGSERIAL PRIMARY KEY,
+  file_type VARCHAR NOT NULL,
+  duration INTEGER NOT NULL,
+  chat_id BIGINT NOT NULL,
+  message_id BIGINT NOT NULL,
+  username VARCHAR,
+  transcript TEXT,
+  language_code VARCHAR,
+  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
+  error TEXT
+);
+
+ALTER TABLE transcription_logs ENABLE ROW LEVEL SECURITY;
+```
+
+### Create a Supabase Edge Function to handle Telegram webhook requests
+
+Next, create a new Edge Function to handle Telegram webhook requests:
+
+```bash
+supabase functions new scribe-bot
+```
+
+If you're using VS Code or Cursor, select `y` when the CLI prompts "Generate VS Code settings for Deno? [y/N]"!
+
+### Set up the environment variables
+
+Within the `supabase/functions` directory, create a new `.env` file and add the following variables:
+
+```env supabase/functions/.env
+# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
+ELEVENLABS_API_KEY=your_api_key
+
+# The bot token you received from the BotFather.
+TELEGRAM_BOT_TOKEN=your_bot_token
+
+# A random secret chosen by you to secure the function.
+FUNCTION_SECRET=random_secret
+```
+
+### Dependencies
+
+The project uses a couple of dependencies:
+
+- The open-source [grammY Framework](https://grammy.dev/) to handle the Telegram webhook requests.
+- The [@supabase/supabase-js](https://supabase.com/docs/reference/javascript) library to interact with the Supabase database.
+- The ElevenLabs [JavaScript SDK](/docs/quickstart) to interact with the speech-to-text API.
+
+Since Supabase Edge Function uses the [Deno runtime](https://deno.land/), you don't need to install the dependencies, rather you can [import](https://docs.deno.com/examples/npm/) them via the `npm:` prefix.
+
+## Code the Telegram bot
+
+In your newly created `scribe-bot/index.ts` file, add the following code:
+
+```ts supabase/functions/scribe-bot/index.ts
+import { Bot, webhookCallback } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
+import 'jsr:@supabase/functions-js/edge-runtime.d.ts'
+import { createClient } from 'jsr:@supabase/supabase-js@2'
+import { ElevenLabsClient } from 'npm:elevenlabs@1.50.5'
+
+console.log(`Function "elevenlabs-scribe-bot" up and running!`)
+
+const elevenLabsClient = new ElevenLabsClient({
+  apiKey: Deno.env.get('ELEVENLABS_API_KEY') || '',
+})
+
+const supabase = createClient(
+  Deno.env.get('SUPABASE_URL') || '',
+  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') || ''
+)
+
+async function scribe({
+  fileURL,
+  fileType,
+  duration,
+  chatId,
+  messageId,
+  username,
+}: {
+  fileURL: string
+  fileType: string
+  duration: number
+  chatId: number
+  messageId: number
+  username: string
+}) {
+  let transcript: string | null = null
+  let languageCode: string | null = null
+  let errorMsg: string | null = null
+  try {
+    const sourceFileArrayBuffer = await fetch(fileURL).then((res) => res.arrayBuffer())
+    const sourceBlob = new Blob([sourceFileArrayBuffer], {
+      type: fileType,
+    })
+
+    const scribeResult = await elevenLabsClient.speechToText.convert({
+      file: sourceBlob,
+      model_id: 'scribe_v1',
+      tag_audio_events: false,
+    })
+
+    transcript = scribeResult.text
+    languageCode = scribeResult.language_code
+
+    // Reply to the user with the transcript
+    await bot.api.sendMessage(chatId, transcript, {
+      reply_parameters: { message_id: messageId },
+    })
+  } catch (error) {
+    errorMsg = error.message
+    console.log(errorMsg)
+    await bot.api.sendMessage(chatId, 'Sorry, there was an error. Please try again.', {
+      reply_parameters: { message_id: messageId },
+    })
+  }
+  // Write log to Supabase.
+  const logLine = {
+    file_type: fileType,
+    duration,
+    chat_id: chatId,
+    message_id: messageId,
+    username,
+    language_code: languageCode,
+    error: errorMsg,
+  }
+  console.log({ logLine })
+  await supabase.from('transcription_logs').insert({ ...logLine, transcript })
+}
+
+const telegramBotToken = Deno.env.get('TELEGRAM_BOT_TOKEN')
+const bot = new Bot(telegramBotToken || '')
+const startMessage = `Welcome to the ElevenLabs Scribe Bot\\! I can transcribe speech in 99 languages with super high accuracy\\!
+    \nTry it out by sending or forwarding me a voice message, video, or audio file\\!
+    \n[Learn more about Scribe](https://elevenlabs.io/speech-to-text) or [build your own bot](https://elevenlabs.io/docs/cookbooks/speech-to-text/telegram-bot)\\!
+  `
+bot.command('start', (ctx) => ctx.reply(startMessage.trim(), { parse_mode: 'MarkdownV2' }))
+
+bot.on([':voice', ':audio', ':video'], async (ctx) => {
+  try {
+    const file = await ctx.getFile()
+    const fileURL = `https://api.telegram.org/file/bot${telegramBotToken}/${file.file_path}`
+    const fileMeta = ctx.message?.video ?? ctx.message?.voice ?? ctx.message?.audio
+
+    if (!fileMeta) {
+      return ctx.reply('No video|audio|voice metadata found. Please try again.')
+    }
+
+    // Run the transcription in the background.
+    EdgeRuntime.waitUntil(
+      scribe({
+        fileURL,
+        fileType: fileMeta.mime_type!,
+        duration: fileMeta.duration,
+        chatId: ctx.chat.id,
+        messageId: ctx.message?.message_id!,
+        username: ctx.from?.username || '',
+      })
+    )
+
+    // Reply to the user immediately to let them know we received their file.
+    return ctx.reply('Received. Scribing...')
+  } catch (error) {
+    console.error(error)
+    return ctx.reply(
+      'Sorry, there was an error getting the file. Please try again with a smaller file!'
+    )
+  }
+})
+
+const handleUpdate = webhookCallback(bot, 'std/http')
+
+Deno.serve(async (req) => {
+  try {
+    const url = new URL(req.url)
+    if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) {
+      return new Response('not allowed', { status: 405 })
+    }
+
+    return await handleUpdate(req)
+  } catch (err) {
+    console.error(err)
+  }
+})
+```
+
+## Deploy to Supabase
+
+If you haven't already, create a new Supabase account at [database.new](https://database.new) and link the local project to your Supabase account:
+
+```bash
+supabase link
+```
+
+### Apply the database migrations
+
+Run the following command to apply the database migrations from the `supabase/migrations` directory:
+
+```bash
+supabase db push
+```
+
+Navigate to the [table editor](https://supabase.com/dashboard/project/_/editor) in your Supabase dashboard and you should see and empty `transcription_logs` table.
+
+![Empty table](/docs/img/guides/functions/elevenlabs/supa-empty-table.png)
+
+Lastly, run the following command to deploy the Edge Function:
+
+```bash
+supabase functions deploy --no-verify-jwt scribe-bot
+```
+
+Navigate to the [Edge Functions view](https://supabase.com/dashboard/project/_/functions) in your Supabase dashboard and you should see the `scribe-bot` function deployed. Make a note of the function URL as you'll need it later, it should look something like `https://<project-ref>.functions.supabase.co/scribe-bot`.
+
+![Edge Function deployed](/docs/img/guides/functions/elevenlabs/supa-edge-function-deployed.png)
+
+### Set up the webhook
+
+Set your bot's webhook URL to `https://<PROJECT_REFERENCE>.functions.supabase.co/telegram-bot` (Replacing `<...>` with respective values). In order to do that, run a GET request to the following URL (in your browser, for example):
+
+```
+https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/setWebhook?url=https://<PROJECT_REFERENCE>.supabase.co/functions/v1/scribe-bot?secret=<FUNCTION_SECRET>
+```
+
+Note that the `FUNCTION_SECRET` is the secret you set in your `.env` file.
+
+![Set webhook](/docs/img/guides/functions/elevenlabs/set-webhook.png)
+
+### Set the function secrets
+
+Now that you have all your secrets set locally, you can run the following command to set the secrets in your Supabase project:
+
+```bash
+supabase secrets set --env-file supabase/functions/.env
+```
+
+## Test the bot
+
+Finally you can test the bot by sending it a voice message, audio or video file.
+
+![Test the bot](/docs/img/guides/functions/elevenlabs/test-bot.png)
+
+After you see the transcript as a reply, navigate back to your table editor in the Supabase dashboard and you should see a new row in your `transcription_logs` table.
+
+![New row in table](/docs/img/guides/functions/elevenlabs/supa-new-row.png)
diff --git a/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx b/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx
index 4fb0eca309be2..c2b95516c0134 100644
--- a/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx
+++ b/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx
@@ -77,7 +77,7 @@ hideToc: true
 
     You can change the schema of your Laravel application by modifying the `search_path` variable `app/config/database.php`.
 
-    Please note the schema you specify in `search_path` has to exist on Supabase. You can create a new schema from the [Table Editor](/dashboard/project/_/editor).
+    The schema you specify in `search_path` has to exist on Supabase. You can create a new schema from the [Table Editor](/dashboard/project/_/editor).
 
     </StepHikeCompact.Details>
 
diff --git a/apps/docs/public/img/guides/functions/elevenlabs/bot-father.png b/apps/docs/public/img/guides/functions/elevenlabs/bot-father.png
new file mode 100644
index 0000000000000..07823ba204736
Binary files /dev/null and b/apps/docs/public/img/guides/functions/elevenlabs/bot-father.png differ
diff --git a/apps/docs/public/img/guides/functions/elevenlabs/set-webhook.png b/apps/docs/public/img/guides/functions/elevenlabs/set-webhook.png
new file mode 100644
index 0000000000000..586a3f1fe2088
Binary files /dev/null and b/apps/docs/public/img/guides/functions/elevenlabs/set-webhook.png differ
diff --git a/apps/docs/public/img/guides/functions/elevenlabs/supa-edge-function-deployed.png b/apps/docs/public/img/guides/functions/elevenlabs/supa-edge-function-deployed.png
new file mode 100644
index 0000000000000..93336f5152153
Binary files /dev/null and b/apps/docs/public/img/guides/functions/elevenlabs/supa-edge-function-deployed.png differ
diff --git a/apps/docs/public/img/guides/functions/elevenlabs/supa-empty-table.png b/apps/docs/public/img/guides/functions/elevenlabs/supa-empty-table.png
new file mode 100644
index 0000000000000..9a418c157e42b
Binary files /dev/null and b/apps/docs/public/img/guides/functions/elevenlabs/supa-empty-table.png differ
diff --git a/apps/docs/public/img/guides/functions/elevenlabs/supa-new-row.png b/apps/docs/public/img/guides/functions/elevenlabs/supa-new-row.png
new file mode 100644
index 0000000000000..937572f60acf3
Binary files /dev/null and b/apps/docs/public/img/guides/functions/elevenlabs/supa-new-row.png differ
diff --git a/apps/docs/public/img/guides/functions/elevenlabs/test-bot.png b/apps/docs/public/img/guides/functions/elevenlabs/test-bot.png
new file mode 100644
index 0000000000000..d3be7d777e4af
Binary files /dev/null and b/apps/docs/public/img/guides/functions/elevenlabs/test-bot.png differ
diff --git a/examples/edge-functions/supabase/.env.local.example b/examples/edge-functions/supabase/.env.local.example
index d04ba13653349..6099e5cc37ed8 100644
--- a/examples/edge-functions/supabase/.env.local.example
+++ b/examples/edge-functions/supabase/.env.local.example
@@ -52,3 +52,6 @@ UPSTASH_REDIS_REST_TOKEN=
 # connect-supabase - https://supabase.com/docs/guides/platform/oauth-apps/publish-an-oauth-app
 SUPA_CONNECT_CLIENT_ID=
 SUPA_CONNECT_CLIENT_SECRET=
+
+# elevenlabs-text-to-speech
+ELEVENLABS_API_KEY=
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/.npmrc b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/.npmrc
new file mode 100644
index 0000000000000..48c6388638017
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/.npmrc
@@ -0,0 +1,3 @@
+# Configuration for private npm package dependencies
+# For more information on using private registries with Edge Functions, see:
+# https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/README.md b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/README.md
new file mode 100644
index 0000000000000..25a44b40f374c
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/README.md
@@ -0,0 +1,52 @@
+## ElevenLabs Scribe Telegram Bot
+
+This is a Telegram bot that uses the ElevenLabs API to transcribe voice messages, as well as audio and video files.
+
+You can find the bot here: https://t.me/ElevenLabsScribeBot
+
+For a detailed tutorial, please see the [ElevenLabs Developer Docs](https://elevenlabs.io/docs/cookbooks/speech-to-text/telegram-bot).
+
+## Requirements
+
+- An ElevenLabs account with an [API key](/app/settings/api-keys).
+- A [Supabase](https://supabase.com) account (you can sign up for a free account via [database.new](https://database.new)).
+- The [Supabase CLI](https://supabase.com/docs/guides/local-development) installed on your machine.
+- The [Deno runtime](https://docs.deno.com/runtime/getting_started/installation/) installed on your machine and optionally [setup in your facourite IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
+- A [Telegram](https://telegram.org) account.
+
+## Setup
+
+### Register the Telegram bot
+
+Next, use [the BotFather](https://t.me/BotFather) to create a new Telegram bot. Run the `/newbot` command and follow the instructions to create a new bot. At the end, you will receive your secret bot token. Note it down securely for the next step.
+
+![BotFather](/assets/images/cookbooks/scribe/telegram-bot/bot-father.png)
+
+### Set up the environment variables
+
+- `cp supabase/functions/.env.example supabase/functions/.env`
+- Update the `.env` file with your values.
+
+## Test locally
+
+- `supabase start`
+- `supabase functions serve --no-verify-jwt --env-file supabase/functions/.env`
+- In another terminal use [ngrok](https://ngrok.com/) to tunnel webhooks to the local server: `ngrok http 54321`
+- Set the bot's webhook url to the ngrok url: `https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/setWebhook?url=https://<NGROK_URL>/functions/v1/elevenlabs-speech-to-text?secret=<FUNCTION_SECRET>`
+
+Note: For background tasks to work locally, you need to set the `per_worker` policy in the [`supabase/config.toml`](./supabase/config.toml) file.
+
+```
+[edge_runtime]
+enabled = true
+policy = "per_worker"
+```
+
+## Deploy
+
+1. Run `supabase link` and link your local project to your Supabase account.
+2. Run `supabase db push` to push the [setup migration](./supabase/migrations/20250203045928_init.sql) to your Supabase database.
+3. Run `supabase functions deploy --no-verify-jwt elevenlabs-speech-to-text`
+4. Run `supabase secrets set --env-file supabase/functions/.env`
+5. Set your bot's webhook url to `https://<PROJECT_REFERENCE>.functions.supabase.co/telegram-bot` (Replacing `<...>` with respective values). In order to do that, run this url (in your browser, for example): `https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/setWebhook?url=https://<PROJECT_REFERENCE>.supabase.co/functions/v1/elevenlabs-speech-to-text?secret=<FUNCTION_SECRET>`
+6. That's it, go ahead and chat with your bot 🤖💬
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/deno.json b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/deno.json
new file mode 100644
index 0000000000000..f6ca8454c5639
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/deno.json
@@ -0,0 +1,3 @@
+{
+  "imports": {}
+}
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts
new file mode 100644
index 0000000000000..67dbc5232922a
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts
@@ -0,0 +1,154 @@
+// Follow this setup guide to integrate the Deno language server with your editor:
+// https://deno.land/manual/getting_started/setup_your_environment
+// This enables autocomplete, go to definition, etc.
+
+// Setup type definitions for built-in Supabase Runtime APIs
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+import { createClient } from "jsr:@supabase/supabase-js@2";
+
+console.log(`Function "elevenlabs-scribe-bot" up and running!`);
+
+import { ElevenLabsClient } from "npm:elevenlabs@1.50.5";
+import {
+  Bot,
+  webhookCallback,
+} from "https://deno.land/x/grammy@v1.34.0/mod.ts";
+
+const elevenLabsClient = new ElevenLabsClient({
+  apiKey: Deno.env.get("ELEVENLABS_API_KEY") || "",
+});
+
+const supabase = createClient(
+  Deno.env.get("SUPABASE_URL") || "",
+  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || "",
+);
+
+async function scribe(
+  { fileURL, fileType, duration, chatId, messageId, username }: {
+    fileURL: string;
+    fileType: string;
+    duration: number;
+    chatId: number;
+    messageId: number;
+    username: string;
+  },
+) {
+  let transcript: string | null = null;
+  let languageCode: string | null = null;
+  let errorMsg: string | null = null;
+  try {
+    const sourceFileArrayBuffer = await fetch(fileURL).then((res) =>
+      res.arrayBuffer()
+    );
+    const sourceBlob = new Blob([sourceFileArrayBuffer], {
+      type: fileType,
+    });
+
+    const scribeResult = await elevenLabsClient.speechToText.convert({
+      file: sourceBlob,
+      model_id: "scribe_v1",
+      tag_audio_events: false,
+    });
+    // console.log({ scribeResult });
+    transcript = scribeResult.text;
+    languageCode = scribeResult.language_code;
+
+    // Reply to the user with the transcript
+    await bot.api.sendMessage(chatId, transcript, {
+      reply_parameters: { message_id: messageId },
+    });
+  } catch (error) {
+    errorMsg = error.message;
+    console.log(errorMsg);
+    await bot.api.sendMessage(
+      chatId,
+      "Sorry, there was an error. Please try again.",
+      {
+        reply_parameters: { message_id: messageId },
+      },
+    );
+  }
+  // Write log to Supabase.
+  const logLine = {
+    file_type: fileType,
+    duration,
+    chat_id: chatId,
+    message_id: messageId,
+    username,
+    language_code: languageCode,
+    error: errorMsg,
+  };
+  console.log({ logLine });
+  await supabase.from("transcription_logs").insert({ ...logLine, transcript });
+}
+
+// Use beforeunload event handler to be notified when function is about to shutdown
+addEventListener("beforeunload", (ev) => {
+  console.log("Function will be shutdown due to", ev.detail?.reason);
+
+  // save state or log the current progress
+});
+
+const telegramBotToken = Deno.env.get("TELEGRAM_BOT_TOKEN");
+const bot = new Bot(telegramBotToken || "");
+const startMessage =
+  `Welcome to the ElevenLabs Scribe Bot\\! I can transcribe speech in 80\\+ languages with super high accuracy\\!
+    \nTry it out by sending or forwarding me a voice message, video, or audio file\\!
+    \n[Learn more about Scribe](https://elevenlabs.io/speech-to-text) or [build your own bot](https://elevenlabs.io/docs/cookbooks/speech-to-text/telegram-bot)\\!
+  `;
+bot.command(
+  "start",
+  (ctx) => ctx.reply(startMessage.trim(), { parse_mode: "MarkdownV2" }),
+);
+
+bot.on([":voice", ":audio", ":video"], async (ctx) => {
+  try {
+    // console.log(ctx);
+    const file = await ctx.getFile();
+    const fileURL =
+      `https://api.telegram.org/file/bot${telegramBotToken}/${file.file_path}`;
+    const fileMeta = ctx.message?.video ?? ctx.message?.voice ??
+      ctx.message?.audio;
+    // console.log({ fileURL, fileMeta });
+    if (!fileMeta) {
+      return ctx.reply(
+        "No video|audio|voice metadata found. Please try again.",
+      );
+    }
+
+    // Run the transcription in the background.
+    EdgeRuntime.waitUntil(
+      scribe({
+        fileURL,
+        fileType: fileMeta.mime_type!,
+        duration: fileMeta.duration,
+        chatId: ctx.chat.id,
+        messageId: ctx.message?.message_id!,
+        username: ctx.from?.username || "",
+      }),
+    );
+
+    // Reply to the user immediately to let them know we received their file.
+    return ctx.reply("Received. Scribing...");
+  } catch (error) {
+    console.error(error);
+    return ctx.reply(
+      "Sorry, there was an error getting the file. Please try again with a smaller file!",
+    );
+  }
+});
+
+const handleUpdate = webhookCallback(bot, "std/http");
+
+Deno.serve(async (req) => {
+  try {
+    const url = new URL(req.url);
+    if (url.searchParams.get("secret") !== Deno.env.get("FUNCTION_SECRET")) {
+      return new Response("not allowed", { status: 405 });
+    }
+
+    return await handleUpdate(req);
+  } catch (err) {
+    console.error(err);
+  }
+});
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/.npmrc b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/.npmrc
new file mode 100644
index 0000000000000..48c6388638017
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/.npmrc
@@ -0,0 +1,3 @@
+# Configuration for private npm package dependencies
+# For more information on using private registries with Edge Functions, see:
+# https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/README.md b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/README.md
new file mode 100644
index 0000000000000..574190a7c5f24
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/README.md
@@ -0,0 +1,120 @@
+# Streaming and Caching Speech with ElevenLabs
+
+Generate and stream speech through Supabase Edge Functions. Store speech in Supabase Storage and cache responses via built-in smart CDN.
+
+## Requirements
+
+- An ElevenLabs account with an [API key](/app/settings/api-keys).
+- A [Supabase](https://supabase.com) account (you can sign up for a free account via [database.new](https://database.new)).
+- The [Supabase CLI](https://supabase.com/docs/guides/local-development) installed on your machine.
+- The [Deno runtime](https://docs.deno.com/runtime/getting_started/installation/) installed on your machine and optionally [setup in your facourite IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
+
+## Setup
+
+### Create a Supabase project locally
+
+After installing the [Supabase CLI](https://supabase.com/docs/guides/local-development), run the following command to create a new Supabase project locally:
+
+```bash
+supabase init
+```
+
+### Configure the storage bucket
+
+You can configure the Supabase CLI to automatically generate a storage bucket by adding this configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[storage.buckets.audio]
+public = false
+file_size_limit = "50MiB"
+allowed_mime_types = ["audio/mp3"]
+objects_path = "./audio"
+```
+
+<Note>
+  Upon running `supabase start` this will create a new storage bucket in your local Supabase
+  project. Should you want to push this to your hosted Supabase project, you can run `supabase seed
+  buckets --linked`.
+</Note>
+
+### Configure background tasks for Supabase Edge Functions
+
+To use background tasks in Supabase Edge Functions when developing locally, you need to add the following configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[edge_runtime]
+policy = "per_worker"
+```
+
+<Note>
+  When running with `per_worker` policy, Function won't auto-reload on edits. You will need to
+  manually restart it by running `supabase functions serve`.
+</Note>
+
+## Run locally
+
+To run the function locally, run the following commands:
+
+```bash
+supabase start
+```
+
+Once the local Supabase stack is up and running, run the following command to start the function and observe the logs:
+
+```bash
+supabase functions serve
+```
+
+## Deploy to Supabase
+
+If you haven't already, create a new Supabase account at [database.new](https://database.new) and link the local project to your Supabase account:
+
+```bash
+supabase link
+```
+
+Once done, run the following command to deploy the function:
+
+```bash
+supabase functions deploy
+```
+
+### Set the function secrets
+
+Now that you have all your secrets set locally, you can run the following command to set the secrets in your Supabase project:
+
+```bash
+supabase secrets set --env-file supabase/functions/.env
+```
+
+## Test the function
+
+The function is designed in a way that it can be used directly as a source for an `<audio>` element.
+
+```html
+<audio
+  src="https://${SUPABASE_PROJECT_REF}.supabase.co/functions/v1/elevenlabs-text-to-speech?text=Hello%2C%20world!&voiceId=JBFqnCBsd6RMkjVDRZzb"
+  controls
+/>
+```
+
+You can find an example frontend implementation in the complete code example on [GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/text-to-speech/supabase/stream-and-cache-storage/src/pages/Index.tsx).
+
+### Try it out
+
+Navigate to `http://127.0.0.1:54321/functions/v1/elevenlabs-text-to-speech?text=hello%20world`.
+
+Afterwards, navigate to `http://127.0.0.1:54323/project/default/storage/buckets/audio` to see the audio file in your local Supabase Storage bucket.
+
+## Test the function
+
+The function is designed in a way that it can be used directly as a source for an `<audio>` element.
+
+```html
+<audio
+  src="https://${SUPABASE_PROJECT_REF}.supabase.co/functions/v1/elevenlabs-text-to-speech?text=Hello%2C%20world!&voiceId=JBFqnCBsd6RMkjVDRZzb"
+  controls
+/>
+```
+
+You can find an example frontend implementation in the complete code example on [GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/text-to-speech/supabase/stream-and-cache-storage/src/pages/Index.tsx).
\ No newline at end of file
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/deno.json b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/deno.json
new file mode 100644
index 0000000000000..f6ca8454c5639
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/deno.json
@@ -0,0 +1,3 @@
+{
+  "imports": {}
+}
diff --git a/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts
new file mode 100644
index 0000000000000..3b1332632b5f2
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts
@@ -0,0 +1,97 @@
+// Setup type definitions for built-in Supabase Runtime APIs
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+import { createClient } from "jsr:@supabase/supabase-js@2";
+import { ElevenLabsClient } from "npm:elevenlabs@1.52.0";
+import * as hash from "npm:object-hash";
+
+const supabase = createClient(
+  Deno.env.get("SUPABASE_URL")!,
+  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
+);
+
+const client = new ElevenLabsClient({
+  apiKey: Deno.env.get("ELEVENLABS_API_KEY"),
+});
+
+// Upload audio to Supabase Storage in a background task
+async function uploadAudioToStorage(
+  stream: ReadableStream,
+  requestHash: string,
+) {
+  const { data, error } = await supabase.storage
+    .from("audio")
+    .upload(`${requestHash}.mp3`, stream, {
+      contentType: "audio/mp3",
+    });
+
+  console.log("Storage upload result", { data, error });
+}
+
+Deno.serve(async (req) => {
+  // To secure your function for production, you can for example validate the request origin,
+  // or append a user access token and validate it with Supabase Auth.
+  console.log("Request origin", req.headers.get("host"));
+  const url = new URL(req.url);
+  const params = new URLSearchParams(url.search);
+  const text = params.get("text");
+  const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";
+
+  const requestHash = hash.MD5({ text, voiceId });
+  console.log("Request hash", requestHash);
+
+  // Check storage for existing audio file
+  const { data } = await supabase
+    .storage
+    .from("audio")
+    .createSignedUrl(`${requestHash}.mp3`, 60);
+
+  if (data) {
+    console.log("Audio file found in storage", data);
+    const storageRes = await fetch(data.signedUrl);
+    if (storageRes.ok) return storageRes;
+  }
+
+  if (!text) {
+    return new Response(
+      JSON.stringify({ error: "Text parameter is required" }),
+      { status: 400, headers: { "Content-Type": "application/json" } },
+    );
+  }
+
+  try {
+    console.log("ElevenLabs API call");
+    const response = await client.textToSpeech.convertAsStream(voiceId, {
+      output_format: "mp3_44100_128",
+      model_id: "eleven_multilingual_v2",
+      text,
+    });
+
+    const stream = new ReadableStream({
+      async start(controller) {
+        for await (const chunk of response) {
+          controller.enqueue(chunk);
+        }
+        controller.close();
+      },
+    });
+
+    // Branch stream to Supabase Storage
+    const [browserStream, storageStream] = stream.tee();
+
+    // Upload to Supabase Storage in the background
+    EdgeRuntime.waitUntil(uploadAudioToStorage(storageStream, requestHash));
+
+    // Return the streaming response immediately
+    return new Response(browserStream, {
+      headers: {
+        "Content-Type": "audio/mpeg",
+      },
+    });
+  } catch (error) {
+    console.log("error", { error });
+    return new Response(JSON.stringify({ error: error.message }), {
+      status: 500,
+      headers: { "Content-Type": "application/json" },
+    });
+  }
+});
diff --git a/supa-mdx-lint/Rule001HeadingCase.toml b/supa-mdx-lint/Rule001HeadingCase.toml
index 6dbdcf7612972..b2b20f49d36b8 100644
--- a/supa-mdx-lint/Rule001HeadingCase.toml
+++ b/supa-mdx-lint/Rule001HeadingCase.toml
@@ -186,6 +186,7 @@ may_uppercase = [
     "Swift",
     "SwiftUI",
     "Team Plan",
+    "Telegram",
     "Third-Party Auth",
     "TimescaleDB",
     "Transformers.js",
diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml
index 4fd0c48e1cdb9..4e9d00102b14e 100644
--- a/supa-mdx-lint/Rule003Spelling.toml
+++ b/supa-mdx-lint/Rule003Spelling.toml
@@ -111,6 +111,7 @@ allow_list = [
     "BigQuery",
     "Bitbucket",
     "Bitwarden",
+    "BotFather",
     "Brevo",
     "CAPTCHA",
     "Cartes Bancaires",
@@ -133,6 +134,7 @@ allow_list = [
     "Django",
     "Docker",
     "Drizzle",
+    "ElevenLabs",
     "EnterpriseDB",
     "Entra",
     "ePHI",


Reviews:
user: login: snyk-io[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-04T09:08:47Z
user: login: laktek
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T03:48:03Z
user: login: thorwebdev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T06:16:38Z
user: login: thorwebdev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T13:16:19Z
user: login: laktek
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T16:23:34Z

Review comments:
diff_hunk: @@ -0,0 +1,97 @@
+// Setup type definitions for built-in Supabase Runtime APIs
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+import { createClient } from "jsr:@supabase/supabase-js@2";
+import { ElevenLabsClient } from "npm:elevenlabs";
+import * as hash from "npm:object-hash";
+
+const supabase = createClient(
+  Deno.env.get("SUPABASE_URL")!,
+  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
+);
+
+const client = new ElevenLabsClient({
+  apiKey: Deno.env.get("ELEVENLABS_API_KEY"),
+});
+
+// Upload audio to Supabase Storage in a background task
+async function uploadAudioToStorage(
+  stream: ReadableStream,
+  requestHash: string,
+) {
+  const { data, error } = await supabase.storage
+    .from("audio")
+    .upload(`${requestHash}.mp3`, stream, {
+      contentType: "audio/mp3",
+    });
+
+  console.log("Storage upload result", { data, error });
+}
+
+Deno.serve(async (req) => {
+  // To secure your function for production, you can for example validate the request origin,
+  // or append a user access token and validate it with Supabase Auth.
+  console.log("Request origin", req.headers.get("host"));
+  const url = new URL(req.url);
+  const params = new URLSearchParams(url.search);
+  const text = params.get("text");
+  const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";
+
+  const requestHash = hash.MD5({ text, voiceId });
+  console.log("Request hash", requestHash);
+
+  // Check storage for existing audio file
+  const { data } = await supabase
+    .storage
+    .from("audio")
+    .createSignedUrl(`${requestHash}.mp3`, 60);
+
+  if (data) {
+    console.log("Audio file found in storage", data);
+    const storageRes = await fetch(data.signedUrl);
path: examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts
user: login: snyk-io[bot]
body: ### <sub><img src="https://res.cloudinary.com/snyk/image/upload/r-d/scm-platform/snyk-pull-requests/severity-high.svg" width="20"/></sub>&nbsp; Server-Side Request Forgery (SSRF)

Unsanitized input from `the request URL` flows into `fetch`, where it is used as an URL to perform a request. This may result in a Server-Side Request Forgery vulnerability.

<h6>
Line 50 | <a href="https://cwe.mitre.org/data/definitions/918.html">CWE-918</a> | <a href="https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/priority-score">Priority score 753</a> | <a href="https://learn.snyk.io/lesson/ssrf-server-side-request-forgery/">Learn more about this vulnerability</h6>

<details>

<summary><b>Data flow: 21 steps</b></summary>

`Step 1 - 3` https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L34

`Step 4 - 5` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L35](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L35)

`Step 6 - 8` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L36](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L36)

`Step 9 - 11` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L39](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L39)

`Step 12` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L40](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L40)

`Step 13 - 14` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L46](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L46)

`Step 15 - 16` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L43](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L43)

`Step 17` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L48](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L48)

`Step 18` [examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L49](https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L49)

`Step 19 - 21` https://github.com/supabase/supabase/blob/9b443ff6dd216d927fb2e6192502f8abc386d4c9/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts#L50

</details>

created_at: 2025-03-04T09:08:47Z
author_association: NONE
diff_hunk: @@ -0,0 +1,244 @@
+---
+title: Streaming Speech with ElevenLabs
+subtitle: Generate and stream speech through Supabase Edge Functions. Store speech in Supabase Storage and cache responses via built-in CDN.
+tocVideo: '4Roog4PAmZ8'
+---
+
+## Introduction
+
+In this tutorial you will learn how to build and edge API to generate, stream, store, and cache speech from text using Supabase Edge Functions, Supabase Storage, and ElevenLabs.`
+
+<Admonition type="tip">
+  Find the [example project on GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/text-to-speech/supabase/stream-and-cache-storage).
+</Admonition>
+
+## Requirements
+
+- An ElevenLabs account with an [API key](/app/settings/api-keys).
+- A [Supabase](https://supabase.com) account (you can sign up for a free account via [database.new](https://database.new)).
+- The [Supabase CLI](https://supabase.com/docs/guides/local-development) installed on your machine.
+- The [Deno runtime](https://docs.deno.com/runtime/getting_started/installation/) installed on your machine and optionally [setup in your facourite IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
+
+## Setup
+
+### Create a Supabase project locally
+
+After installing the [Supabase CLI](https://supabase.com/docs/guides/local-development), run the following command to create a new Supabase project locally:
+
+```bash
+supabase init
+```
+
+### Configure the storage bucket
+
+You can configure the Supabase CLI to automatically generate a storage bucket by adding this configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[storage.buckets.audio]
+public = false
+file_size_limit = "50MiB"
+allowed_mime_types = ["audio/mp3"]
+objects_path = "./audio"
+```
+
+<Admonition type="tip">
+  Upon running `supabase start` this will create a new storage bucket in your local Supabase
+  project. Should you want to push this to your hosted Supabase project, you can run `supabase seed
+  buckets --linked`.
+</Admonition>
+
+### Configure background tasks for Supabase Edge Functions
+
+To use background tasks in Supabase Edge Functions when developing locally, you need to add the following configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[edge_runtime]
+policy = "per_worker"
+```
+
+<Admonition type="tip">
+  When running with `per_worker` policy, Function won't auto-reload on edits. You will need to
+  manually restart it by running `supabase functions serve`.
+</Admonition>
+
+### Create a Supabase Edge Function for Speech generation
+
+Create a new Edge Function by running the following command:
+
+```bash
+supabase functions new text-to-speech
+```
+
+If you're using VS Code or Cursor, select `y` when the CLI prompts "Generate VS Code settings for Deno? [y/N]"!
+
+### Set up the environment variables
+
+Within the `supabase/functions` directory, create a new `.env` file and add the following variables:
+
+```env supabase/functions/.env
+# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
+ELEVENLABS_API_KEY=your_api_key
+```
+
+### Dependencies
+
+The project uses a couple of dependencies:
+
+- The [@supabase/supabase-js](https://supabase.com/docs/reference/javascript) library to interact with the Supabase database.
+- The ElevenLabs [JavaScript SDK](/docs/quickstart) to interact with the text-to-speech API.
+- The open-source [object-hash](https://www.npmjs.com/package/object-hash) to generate a hash from the request parameters.
+
+Since Supabase Edge Function uses the [Deno runtime](https://deno.land/), you don't need to install the dependencies, rather you can [import](https://docs.deno.com/examples/npm/) them via the `npm:` prefix.
+
+## Code the Supabase Edge Function
+
+In your newly created `supabase/functions/text-to-speech/index.ts` file, add the following code:
+
+```ts supabase/functions/text-to-speech/index.ts
+// Setup type definitions for built-in Supabase Runtime APIs
+import 'jsr:@supabase/functions-js/edge-runtime.d.ts';
+import { createClient } from 'jsr:@supabase/supabase-js@2';
+import { ElevenLabsClient } from 'npm:elevenlabs';
path: apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx
user: login: laktek
body: nit: let's specify versions for npm dependencies
created_at: 2025-03-05T03:47:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+// Setup type definitions for built-in Supabase Runtime APIs
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+import { createClient } from "jsr:@supabase/supabase-js@2";
+import { ElevenLabsClient } from "npm:elevenlabs";
+import * as hash from "npm:object-hash";
+
+const supabase = createClient(
+  Deno.env.get("SUPABASE_URL")!,
+  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
+);
+
+const client = new ElevenLabsClient({
+  apiKey: Deno.env.get("ELEVENLABS_API_KEY"),
+});
+
+// Upload audio to Supabase Storage in a background task
+async function uploadAudioToStorage(
+  stream: ReadableStream,
+  requestHash: string,
+) {
+  const { data, error } = await supabase.storage
+    .from("audio")
+    .upload(`${requestHash}.mp3`, stream, {
+      contentType: "audio/mp3",
+    });
+
+  console.log("Storage upload result", { data, error });
+}
+
+Deno.serve(async (req) => {
+  // To secure your function for production, you can for example validate the request origin,
+  // or append a user access token and validate it with Supabase Auth.
+  console.log("Request origin", req.headers.get("host"));
+  const url = new URL(req.url);
+  const params = new URLSearchParams(url.search);
+  const text = params.get("text");
+  const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";
+
+  const requestHash = hash.MD5({ text, voiceId });
+  console.log("Request hash", requestHash);
+
+  // Check storage for existing audio file
+  const { data } = await supabase
+    .storage
+    .from("audio")
+    .createSignedUrl(`${requestHash}.mp3`, 60);
+
+  if (data) {
+    console.log("Audio file found in storage", data);
+    const storageRes = await fetch(data.signedUrl);
path: examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts
user: login: thorwebdev
body: Don't think this is applicable here. 
created_at: 2025-03-05T06:16:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,244 @@
+---
+title: Streaming Speech with ElevenLabs
+subtitle: Generate and stream speech through Supabase Edge Functions. Store speech in Supabase Storage and cache responses via built-in CDN.
+tocVideo: '4Roog4PAmZ8'
+---
+
+## Introduction
+
+In this tutorial you will learn how to build and edge API to generate, stream, store, and cache speech from text using Supabase Edge Functions, Supabase Storage, and ElevenLabs.`
+
+<Admonition type="tip">
+  Find the [example project on GitHub](https://github.com/elevenlabs/elevenlabs-examples/tree/main/examples/text-to-speech/supabase/stream-and-cache-storage).
+</Admonition>
+
+## Requirements
+
+- An ElevenLabs account with an [API key](/app/settings/api-keys).
+- A [Supabase](https://supabase.com) account (you can sign up for a free account via [database.new](https://database.new)).
+- The [Supabase CLI](https://supabase.com/docs/guides/local-development) installed on your machine.
+- The [Deno runtime](https://docs.deno.com/runtime/getting_started/installation/) installed on your machine and optionally [setup in your facourite IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
+
+## Setup
+
+### Create a Supabase project locally
+
+After installing the [Supabase CLI](https://supabase.com/docs/guides/local-development), run the following command to create a new Supabase project locally:
+
+```bash
+supabase init
+```
+
+### Configure the storage bucket
+
+You can configure the Supabase CLI to automatically generate a storage bucket by adding this configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[storage.buckets.audio]
+public = false
+file_size_limit = "50MiB"
+allowed_mime_types = ["audio/mp3"]
+objects_path = "./audio"
+```
+
+<Admonition type="tip">
+  Upon running `supabase start` this will create a new storage bucket in your local Supabase
+  project. Should you want to push this to your hosted Supabase project, you can run `supabase seed
+  buckets --linked`.
+</Admonition>
+
+### Configure background tasks for Supabase Edge Functions
+
+To use background tasks in Supabase Edge Functions when developing locally, you need to add the following configuration in the `config.toml` file:
+
+```toml ./supabase/config.toml
+[edge_runtime]
+policy = "per_worker"
+```
+
+<Admonition type="tip">
+  When running with `per_worker` policy, Function won't auto-reload on edits. You will need to
+  manually restart it by running `supabase functions serve`.
+</Admonition>
+
+### Create a Supabase Edge Function for Speech generation
+
+Create a new Edge Function by running the following command:
+
+```bash
+supabase functions new text-to-speech
+```
+
+If you're using VS Code or Cursor, select `y` when the CLI prompts "Generate VS Code settings for Deno? [y/N]"!
+
+### Set up the environment variables
+
+Within the `supabase/functions` directory, create a new `.env` file and add the following variables:
+
+```env supabase/functions/.env
+# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
+ELEVENLABS_API_KEY=your_api_key
+```
+
+### Dependencies
+
+The project uses a couple of dependencies:
+
+- The [@supabase/supabase-js](https://supabase.com/docs/reference/javascript) library to interact with the Supabase database.
+- The ElevenLabs [JavaScript SDK](/docs/quickstart) to interact with the text-to-speech API.
+- The open-source [object-hash](https://www.npmjs.com/package/object-hash) to generate a hash from the request parameters.
+
+Since Supabase Edge Function uses the [Deno runtime](https://deno.land/), you don't need to install the dependencies, rather you can [import](https://docs.deno.com/examples/npm/) them via the `npm:` prefix.
+
+## Code the Supabase Edge Function
+
+In your newly created `supabase/functions/text-to-speech/index.ts` file, add the following code:
+
+```ts supabase/functions/text-to-speech/index.ts
+// Setup type definitions for built-in Supabase Runtime APIs
+import 'jsr:@supabase/functions-js/edge-runtime.d.ts';
+import { createClient } from 'jsr:@supabase/supabase-js@2';
+import { ElevenLabsClient } from 'npm:elevenlabs';
path: apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx
user: login: thorwebdev
body: Done ✅ 
created_at: 2025-03-05T13:16:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-04T09:07:30Z
body: 
Thanks for contributing to Supabase! ❤️ Our team will review your PR.

A few tips for a smoother review process:
- If you have a local version of the repo, run `pnpm run format` to make sure formatting checks pass.
- Once we've reviewed your PR, please don't trivially merge master (don't click `Update branch` if there are no merge conflicts to be fixed). This invalidates any pre-merge checks we've run.

author_association: CONTRIBUTOR
user: login: vercel[bot]
created_at: 2025-03-04T09:07:36Z
body: [vc]: #+D6l9CE2pugbGmcDZzD2LwgmGUxhOoyo+q+pzbOn85w=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJkZXNpZ24tc3lzdGVtIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvZGVzaWduLXN5c3RlbSIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zdXBhYmFzZS9kZXNpZ24tc3lzdGVtLzdTWWEzZWhRQzFFVUpMZ21NZll2RW9SMmdIMWUiLCJwcmV2aWV3VXJsIjoiZGVzaWduLXN5c3RlbS1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZXYtYTI4OTVmLXN1cGFiYXNlLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiZGVzaWduLXN5c3RlbS1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZXYtYTI4OTVmLXN1cGFiYXNlLnZlcmNlbC5hcHAifX0seyJuYW1lIjoic3R1ZGlvLXN0YWdpbmciLCJyb290RGlyZWN0b3J5IjoiYXBwcy9zdHVkaW8iLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc3VwYWJhc2Uvc3R1ZGlvLXN0YWdpbmcvM2VyU3RRZHpKN21manlMSHNVVGN4R3lYenRSdSIsInByZXZpZXdVcmwiOiJzdHVkaW8tc3RhZ2luZy1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZS1mZDIyOTMtc3VwYWJhc2UudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJzdHVkaW8tc3RhZ2luZy1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZS1mZDIyOTMtc3VwYWJhc2UudmVyY2VsLmFwcCJ9fSx7Im5hbWUiOiJkb2NzIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvZG9jcyIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zdXBhYmFzZS9kb2NzL0ZTZUNMOG5vQkxHTDJ4SHhMemlDYkpTTXJtZzkiLCJwcmV2aWV3VXJsIjoiZG9jcy1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZXZlbmxhYnMtc3QtMjQ5OGE4LXN1cGFiYXNlLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiZG9jcy1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZXZlbmxhYnMtc3QtMjQ5OGE4LXN1cGFiYXNlLnZlcmNlbC5hcHAifX0seyJuYW1lIjoic3R1ZGlvIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvc3R1ZGlvIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3N1cGFiYXNlL3N0dWRpby9CbVhKY3ZlZzZCdkxSMW13RmtHTnVBYVR3Z0xSIiwicHJldmlld1VybCI6InN0dWRpby1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWVsZXZlbmxhYnMtYjdlYzNhLXN1cGFiYXNlLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifX0seyJuYW1lIjoic3R1ZGlvLXNlbGYtaG9zdGVkIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvc3R1ZGlvIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3N1cGFiYXNlL3N0dWRpby1zZWxmLWhvc3RlZC85UE1YRG43c3JFSlJVUjd1NVRqZE16RnJvdmpnIiwicHJldmlld1VybCI6InN0dWRpby1zZWxmLWhvc3RlZC1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWNhNjhiYS1zdXBhYmFzZS52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6InN0dWRpby1zZWxmLWhvc3RlZC1naXQtZm9yay10aG9yd2ViZGV2LXRob3ItYWRkLWNhNjhiYS1zdXBhYmFzZS52ZXJjZWwuYXBwIn19LHsibmFtZSI6InpvbmUtd3d3LWRvdC1jb20iLCJyb290RGlyZWN0b3J5IjoiYXBwcy93d3ciLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc3VwYWJhc2Uvem9uZS13d3ctZG90LWNvbS8zZ1J0Rlc1ZlZzQVA4UEJmcGdROWVzZTJrVWRqIiwicHJldmlld1VybCI6InpvbmUtd3d3LWRvdC1jb20tZ2l0LWZvcmstdGhvcndlYmRldi10aG9yLWFkZC1lLTkwM2MwYi1zdXBhYmFzZS52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6InpvbmUtd3d3LWRvdC1jb20tZ2l0LWZvcmstdGhvcndlYmRldi10aG9yLWFkZC1lLTkwM2MwYi1zdXBhYmFzZS52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **design-system** | ✅ Ready ([Inspect](https://vercel.com/supabase/design-system/7SYa3ehQC1EUJLgmMfYvEoR2gH1e)) | [Visit Preview](https://design-system-git-fork-thorwebdev-thor-add-elev-a2895f-supabase.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/design-system-git-fork-thorwebdev-thor-add-elev-a2895f-supabase.vercel.app?via=pr-comment-feedback-link) | Mar 7, 2025 8:03am |
| **docs** | ✅ Ready ([Inspect](https://vercel.com/supabase/docs/FSeCL8noBLGL2xHxLziCbJSMrmg9)) | [Visit Preview](https://docs-git-fork-thorwebdev-thor-add-elevenlabs-st-2498a8-supabase.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/docs-git-fork-thorwebdev-thor-add-elevenlabs-st-2498a8-supabase.vercel.app?via=pr-comment-feedback-link) | Mar 7, 2025 8:03am |
| **studio-self-hosted** | ✅ Ready ([Inspect](https://vercel.com/supabase/studio-self-hosted/9PMXDn7srEJRUR7u5TjdMzFrovjg)) | [Visit Preview](https://studio-self-hosted-git-fork-thorwebdev-thor-add-ca68ba-supabase.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/studio-self-hosted-git-fork-thorwebdev-thor-add-ca68ba-supabase.vercel.app?via=pr-comment-feedback-link) | Mar 7, 2025 8:03am |
| **studio-staging** | ✅ Ready ([Inspect](https://vercel.com/supabase/studio-staging/3erStQdzJ7mfjyLHsUTcxGyXztRu)) | [Visit Preview](https://studio-staging-git-fork-thorwebdev-thor-add-ele-fd2293-supabase.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/studio-staging-git-fork-thorwebdev-thor-add-ele-fd2293-supabase.vercel.app?via=pr-comment-feedback-link) | Mar 7, 2025 8:03am |
| **zone-www-dot-com** | ✅ Ready ([Inspect](https://vercel.com/supabase/zone-www-dot-com/3gRtFW5fVsAP8PBfpgQ9ese2kUdj)) | [Visit Preview](https://zone-www-dot-com-git-fork-thorwebdev-thor-add-e-903c0b-supabase.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/zone-www-dot-com-git-fork-thorwebdev-thor-add-e-903c0b-supabase.vercel.app?via=pr-comment-feedback-link) | Mar 7, 2025 8:03am |

<details><summary>1 Skipped Deployment</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **studio** | ⬜️ Ignored ([Inspect](https://vercel.com/supabase/studio/BmXJcveg6BvLR1mwFkGNuAaTwgLR)) | [Visit Preview](https://studio-git-fork-thorwebdev-thor-add-elevenlabs-b7ec3a-supabase.vercel.app) |  | Mar 7, 2025 8:03am |
</details>







author_association: NONE
user: login: vercel[bot]
created_at: 2025-03-04T09:07:37Z
body: @thorwebdev is attempting to deploy a commit to the **Supabase** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=Supabase&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%229b443ff6dd216d927fb2e6192502f8abc386d4c9%22%7D%2C%22id%22%3A%22QmTahBGM5dqy6Vm77wyk3fXdsL2oN5TnGrdptcPEvhQ8Kp%22%2C%22org%22%3A%22supabase%22%2C%22prId%22%3A33989%2C%22repo%22%3A%22supabase%22%7D).


author_association: NONE
user: login: snyk-io[bot]
created_at: 2025-03-04T09:08:45Z
body: ![](https://res.cloudinary.com/snyk/image/upload/r-d/scm-platform/snyk-pull-requests/pr-banner-default.svg)

### :no_entry: **Snyk checks have failed. 1 issues have been found so far.**

| Icon     | Severity | Issues  |
| -------- | -------- | ------- |
| ![](https://res.cloudinary.com/snyk/image/upload/r-d/scm-platform/snyk-pull-requests/severity-critical.svg) | Critical | 0 |
| ![](https://res.cloudinary.com/snyk/image/upload/r-d/scm-platform/snyk-pull-requests/severity-high.svg) | **High** | **1** |
| ![](https://res.cloudinary.com/snyk/image/upload/r-d/scm-platform/snyk-pull-requests/severity-medium.svg) | Medium | 0 |
| ![](https://res.cloudinary.com/snyk/image/upload/r-d/scm-platform/snyk-pull-requests/severity-low.svg) | Low | 0 |

:no_entry: **code/snyk** check is complete. 1 issues have been found. [(View Details)](https://app.snyk.io/org/supabase-default/project/d7233d2d-985d-4652-abdd-2095e98ef81e/pr-check/7186ff41-b673-4171-814f-7530826f2e49?refs=c04ab7ba24fdb87fcf0449fcf1ec391c0d10716e&source=prChecksComment)


author_association: NONE