’s Async API’s provide a REST interface that helps you to submit any recorded or saved conversations to When you submit a conversation, you’ll receive a Conversation ID unique to your conversation, and a unique Job ID which can be used to determine status and completion of’s asyncronous conversation processing. Retrieving a job status can be accomplished either manually using the GET /job-status endpoint, or by passing a webhookUrl parameter in the initial async POST request in order to receive automatic job updates. This blog will focus on best practices for configuring webhooks.

Sign up

The first step to exploring the differences is to register for an account at Symbl (i.e., Grab both your appId and your appSecret.


With both your appId and your appSecret you authenticate either with a cURL command or with Postman so that you receive your token. Here is an example with cURL`:

curl -k -X POST "" \      -H "accept: application/json" \      -H "Content-Type: application/json" \      -d "{ \"type\": \"application\", \"appId\": \"<appId>\", \"appSecret\": \"<appSecret>\"}"

After authenticating you receive your token, which will can be used to authorize requests Async API endpoints as Bearer Authorization.

Ideally a token server would handle authentication (with code that makes RESTful API call for generating token) so that neither the appSecret nor the appId were ever exposed. However, cURL sets you up immediately anyway. With the Bearer token handy you are now ready to establish a WebSocket endpoint for performing live transcription.

Webhooks Expected Response

For each specific job you will receive three status update webhook requests, each containing the Job ID they pertain to. These are:

  • scheduled – notice that your request has been received and is being prepped for processing.
{ id: '', status: 'scheduled' }
  • in_progress – notice that is processing your conversation to generate a transcript and insights.
{ id: '', status: 'in_progress' }
  • completed – notice that the job has been completed, and you are free to make additional requests to the Conversation API for retrieveal of the job output.
{ id: '', status: 'completed' }

The webhookUrl Parameter

For requests to the Async API the webhookUrl parameter should be passed as a query param for requests containing local files, and in the body of the request for submissions providing the file contents via URL. For example, here is a sample cURL request using the Async Audio API endpoint.

curl --location --request POST '' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer $ACCESS_TOKEN' \ --data-raw '{ "url": "", "webhookUrl": "" }'


{ "conversationId": "<conversationId>", "jobId": "<jobId>" }

Webhooks Server Configuration

There are two main requirements for a webhook server to be compatible with Symbl’s API’s:

1. The url must be publicly accessible.

2. The server must respond with either a status 200 or 201 upon receipt of successful POST requests. It is best practice to have the server respond immediately, before any other application logic to avoid unnecessary retries. See sample implementation below for an example.

Webhooks Server Sample Implementation

This is a sample implementation of a simple Node.js server using Express to listen over localhost. In this sample we’ll use ngrok to provide a HTTP tunnel to your localhost port.

// Require express and body-parser const express = require('express'); const bodyParser = require('body-parser')   // Init and define a port const app = express(); const PORT = 8000   // Let express know to use body-parsers JSON parser option app.use(bodyParser.json())   // Start express at PORT app.listen(PORT, () => console.log(`Listening at port ${PORT}`));   // Handle POST to your express server'/',(request,response) => { // Send status 200 (or 201) response quickly to avoid retry response.sendStatus(200);   // Handle job status request let data = request.body; console.log('jobId',,' status:', data.status);   if (data.status === 'completed') { console.log(`status is complete for jobId:${}`) //logic for handling completed job here }   if (data.status === 'failed') { console.log(`status is complete for jobId:${}`) //logic for handling failed job here } })

Once the Express server is booted up and listening, start ngrok with the terminal command:

$ ./ngrok http 8000

ngrok will provide a forwarding url exposing your localhost as an external url, which will be used as the webhookUrl parameter in your request to Symbl’s Async API.

ngrok by @inconshreveable                                                            (Ctrl+C to quit)  Session Status                online Session Expires               1 hour, 57 minutes Update                        update available (version 2.3.40, Ctrl-U to update) Version                       2.3.35 Region                        United States (us) Web Interface        Forwarding           -> http://localhost:8000 Forwarding           -> http://localhost:8000  Connections                   ttl     opn     rt1     rt5     p50     p90                               0       0       0.00    0.00    0.00    0.00

Once a request is submitted to the Async API, you will see the webhook requests appear as console output from the node.js Express server, as well as in the ngrok terminal.

What’s Next

When a status: ‘completed’ event is received the conversation transcript & insights will be available via the Conversation API. Use the Conversation ID returned in the initial Async API request for retrieval. For full automation of this process additional logic can be added to the node.js Express server to handle these requests to the Conversation API upon job completion.

Join Our Community‘s invites developers to reach out to us via email at, join our Slack channels, participate in our hackathons, fork our Postman public workspace, or git clone our repos at’s GitHub.