{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition","partial"]},"type":"markdown"},"seo":{"title":"Use Webhooks to Trigger Job Email Notifications with Node.js, SendGrid and Express","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"use-webhooks-to-trigger-job-email-notifications-with-nodejs-sendgrid-and-express","__idx":0},"children":["Use Webhooks to Trigger Job Email Notifications with Node.js, SendGrid and Express"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["By Vikram Vaswani, Developer Advocate - Apr 05, 2022"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"introduction","__idx":1},"children":["Introduction"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhooks are a common mechanism to asynchronously transfer information between multiple Web applications. The idea is simple: when a specific event occurs in an application, the application invokes a URL (the \"webhook\") in a downstream application and sends it a message containing relevant data. The downstream application uses the invocation of the webhook URL and the data passed to it to trigger a further process - this could be sending an email, updating a database record or even invoking another webhook."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhooks are a more efficient alternative to repeatedly pinging an application for event status, and they are well-supported in all the Rev AI APIs. This tutorial gets you started with Rev AI API webhooks, showing you how to implement a simple webhook that triggers an email notification when a user's transcription job completes."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"assumptions","__idx":2},"children":["Assumptions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This tutorial assumes that:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You have a Rev AI account and access token. If not, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.rev.ai/auth/signup"},"children":["sign up for a free account"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/get-started#step-1-get-your-access-token"},"children":["generate an access token"]},"."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You have a Twilio SendGrid account and API key. If not, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://signup.sendgrid.com/"},"children":["sign up for a free account"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://app.sendgrid.com/settings/api_keys"},"children":["generate a Twilio SendGrid API key"]},". In order to send email through Twilio SendGrid, you must also ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.sendgrid.com/ui/sending-email/sender-verification"},"children":["verify your sender email address"]},"."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You have a properly-configured Node.js development environment with Node.js v16.x or v17.x. If not, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://nodejs.org/en/download/"},"children":["download and install Node.js"]}," for your operating system."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You have some familiarity with the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://expressjs.com/"},"children":["Express framework"]},". If not, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://expressjs.com/en/starter/hello-world.html"},"children":["familiarize yourself with the basics using this example application"]},"."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your webhook will be available at a public URL. If not, or if you prefer to develop and test locally, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://ngrok.com/"},"children":["download and install ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ngrok"]}]}," to generate a temporary public URL for your webhook."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You have an audio file to transcribe. If not, use this ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.rev.ai/FTC_Sample_1.mp3"},"children":["example audio file from Rev AI"]},"."]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SendGrid API key should be configured with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Full Access"]}," privilege. This setting can configured at the time of creating the API key."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1-install-required-packages","__idx":3},"children":["Step 1: Install required packages"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This tutorial will use:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/sdk/node"},"children":["Rev AI Node SDK"]},", to submit transcription requests to the Rev AI ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api/asynchronous"},"children":["Asynchronous Speech-to-Text API"]},";"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.npmjs.com/package/@sendgrid/mail"},"children":["Twilio SendGrid service"]},", to send email notifications;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://expressjs.com/"},"children":["Express Web framework"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.npmjs.com/package/body-parser"},"children":["body-parser middleware"]},", to receive and parse webhook requests."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Begin by installing the required packages:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npm i revai-node-sdk @sendgrid/mail express body-parser\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2-create-the-webhook-handler","__idx":4},"children":["Step 2: Create the webhook handler"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The webhook URL is specified as part of the job parameters submitted to the Rev AI API. On job completion, the Rev AI API will send an HTTP POST request containing JSON-encoded details of the completed job to the webhook URL. Here is an example of one such POST request:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"{\n  \"job\": {\n    \"id\": \"8xBckmk6rAqu\",\n    \"created_on\": \"2022-03-16T14:26:14.151Z\",\n    \"completed_on\": \"2022-03-16T14:26:53.601Z\",\n    \"name\": \"FTC_Sample_1.mp3\",\n    \"notification_config\": {\"url\": \"https://webhook.site/84de11c2-e60b-4c15-928d-969cd26ce3cc\"},\n    \"source_config\": {\"url\": \"https://www.rev.ai/FTC_Sample_1.mp3\"},\n    \"status\": \"transcribed\",\n    \"duration_seconds\": 107,\n    \"type\": \"async\",\n    \"language\": \"en\"\n  }\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following example demonstrates how to implement a webhook handler that receives and parses the HTTP POST message from the Rev AI API and sends an email notification using Express and the Twilio SendGrid API client."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To use this example, you must first replace three placeholders:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<SENDER_EMAIL_ADDRESS>"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<RECIPIENT_EMAIL_ADDRESS>"]}," for the sender and recipient email addresses; and"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<SENDGRID_API_KEY>"]}," for the Twilio SendGrid API key."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const bodyParser = require('body-parser');\nconst express = require('express');\nconst sendgrid = require('@sendgrid/mail');\n\n// Twilio SendGrid API key\nconst sendgridKey = '<SENDGRID_API_KEY>';\n// sender email address\nconst senderEmail = '<SENDER_EMAIL>';\n// recipient email address\nconst receiverEmail = '<RECEIVER_EMAIL>';\n\n// set API key for SendGrid\nsendgrid.setApiKey(sendgridKey);\n\n// create Express application\nconst app = express();\napp.use(bodyParser.json());\n\n// handle requests to webhook endpoint\napp.post('/hook', async req => {\n  const job = req.body.job;\n  console.log(`Received status for job id ${job.id}: ${job.status}`);    \n\n  const message = {\n    from: senderEmail,\n    to: receiverEmail,\n    subject: `Job ${job.id} is COMPLETE`,\n    text: job.status === 'transcribed'\n        ? `Log in at https://rev.ai/jobs/speech-to-text/ to collect your transcript.`\n        : `An error occurred. Log in at https://rev.ai/jobs/speech-to-text/ to view details.`\n  };\n\n  try {\n    await sendgrid.send(message);\n    console.log('Email successfully sent');\n  } catch (e) {\n    console.error(e);\n  }\n\n});\n\n//  start application on port 3000\napp.listen(3000, () => {\n  console.log('Webhook listening');\n})\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save this code listing as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["index.js"]}," and take a closer look at it:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["This code listing begins by importing the required packages and credentials and creating a Twilio SendGrid API client."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["It starts an Express application on port 3000 and waits for incoming POST requests to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/hook"]}," URL route."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["When the application receives a POST request at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/hook"]},", it parses the incoming JSON message body and checks the job ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["It prepares an email message with recipient, subject and - depending on the status - appropriate message body."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["It calls the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sendgrid.send()"]}," method to deliver the message via the Twilio SendGrid API."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Errors, if any, in mail delivery are sent to the console."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3-test-the-webhook","__idx":5},"children":["Step 3: Test the webhook"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To see the webhook in action, first ensure that you have replaced the placeholders as described in the previous step and then start the application using the command below."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"node index.js\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Next, submit an audio file for transcription to Rev AI and include the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["notification_config"]}," parameter with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["url"]}," option in your request. This parameter specifies the webhook URL that the Rev AI API should invoke on job completion."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here is an example of submitting an audio file with a webhook using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST \"https://api.rev.ai/speechtotext/v1/jobs\" \\\n     -H \"Authorization: Bearer <REVAI_ACCESS_TOKEN>\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"source_config\": {\"url\": \"<URL>\"},\"notification_config\": {\"url\": \"http://<WEBHOOK-HOST>/hook\"}}'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you prefer to submit the audio file using the Rev AI Node SDK, use this script instead (note this does not support authorization headers for the source or notification urls):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const { RevAiApiClient } = require('revai-node-sdk');\n\nconst revAiToken = '<REVAI_ACCESS_TOKEN>';\nconst webhookUrl = 'http://<WEBHOOK-HOST>/hook';\nconst fileUrl = '<URL>';\n\n// define job options\nconst jobOptions = {\n  notification_config: {\n    url: webhookUrl\n  }\n};\n\n// create Rev AI API client\nconst revAiClient = new RevAiApiClient(revAiToken);\n\n// submit job\njob = revAiClient.submitJobUrl(fileUrl, jobOptions);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In both cases, replace the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<REVAI_ACCESS_TOKEN>"]}," placeholder with your Rev AI access token and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<URL>"]}," placeholder with the direct URL to your audio file. Additionally, replace the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<WEBHOOK-HOST>"]}," placeholder as follows:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If you are developing and testing in the public cloud, your Express application will typically be available at a public domain or IP address. In this case, replace the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<WEBHOOK-HOST>"]}," placeholder with the correct domain name or IP address, including the port number ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["3000"]}," if required."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If you are developing and testing locally, your Express application will not be available publicly and you must therefore configure a public forwarding URL using a tool like ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ngrok"]},". Obtain this URL using the command ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ngrok http 3000"]}," and replace the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<WEBHOOK-HOST>"]}," placeholder with the temporary forwarding URL generated by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ngrok"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once the job is processed, the Rev AI API will send a POST request to the webhook URL and shortly after, the recipient will receive an email notification."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If a webhook doesn't work as expected, you can ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/resources/tutorials/get-started-api-webhooks"},"children":["test and inspect the webhook response"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":6},"children":["Next steps"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Learn more about using webhooks for asynchronous processing by visiting the following links:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Documentation: Asynchronous Speech-To-Text API ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api/asynchronous"},"children":["job submission"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api/asynchronous/webhooks"},"children":["webhooks"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Documentation: ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://ngrok.com/docs#getting-started-expose"},"children":["Using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ngrok"]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tutorial: ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/resources/tutorials/get-started-api-webhooks"},"children":["Get Started with Rev AI API Webhooks"]}]}]}]},"headings":[{"value":"Use Webhooks to Trigger Job Email Notifications with Node.js, SendGrid and Express","id":"use-webhooks-to-trigger-job-email-notifications-with-nodejs-sendgrid-and-express","depth":1},{"value":"Introduction","id":"introduction","depth":2},{"value":"Assumptions","id":"assumptions","depth":2},{"value":"Step 1: Install required packages","id":"step-1-install-required-packages","depth":2},{"value":"Step 2: Create the webhook handler","id":"step-2-create-the-webhook-handler","depth":2},{"value":"Step 3: Test the webhook","id":"step-3-test-the-webhook","depth":2},{"value":"Next steps","id":"next-steps","depth":2}],"frontmatter":{"title":"Use Webhooks to Trigger Job Email Notifications with Node.js, SendGrid and Express","description":"Notify users of job completion status via email using webhooks","date":"2022-04-05T00:00:00.000Z","byline":"Vikram Vaswani, Developer Advocate","disableLastModified":false,"toc":{"enable":true},"seo":{"title":"Use Webhooks to Trigger Job Email Notifications with Node.js, SendGrid and Express"}},"lastModified":"2026-02-24T14:47:49.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/resources/tutorials/send-email-notifications-webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}