Gareth Wilson Gareth Wilson

Guide to VideoAsk Webhooks: Features and Best Practices

Published


VideoAsk webhooks send each response to your own systems as soon as it's ready. When someone completes a videoask, or abandons it part-way, VideoAsk can post their answers, contact details and links to their video or audio to a URL you choose, and it can do the same when the transcription is finished or when you and a contact exchange messages. Teams use this to put leads into a CRM, start a hiring workflow, or store recordings and transcripts in their own systems.

This guide covers how to set up VideoAsk webhooks in the app or through the API, the events and payload shape, how to authenticate requests with a secret header, and the timing details that cause duplicate or late webhooks.

What are VideoAsk webhooks?

A VideoAsk webhook belongs to one videoask (VideoAsk's API calls it a form). You choose a target URL and the events that trigger it, and VideoAsk sends a JSON POST each time one happens. Webhooks are available on paid plans.

You can create them in the videoask's Connect tab, set organisation-wide defaults that every new videoask inherits, or manage them with the Webhooks API, where each webhook is identified by a tag.

VideoAsk doesn't sign webhooks with a documented algorithm. Instead, both the Connect tab and the API let you add a custom header with a secret value, and VideoAsk sends that header with each request. VideoAsk's help article describes the header as "signed by VideoAsk", but what you configure is a static header name and value, and that value is what your receiver compares.

VideoAsk webhook features

FeatureDetails
AvailabilityPaid plans
ConfigurationConnect tab > Webhooks, organisation default webhook settings, or PUT /forms/{form_id}/webhooks/{tag}
Method and formatPOST, JSON
Event fieldevent_type
AuthenticationA secret header you set (Add a secret, or headers in the API). No signature
SuccessAny 2xx
Retries3, after 5, 10 and 20 minutes
Idle deadline60 minutes by default, 15 to 240 configurable
TestingView deliveries > Send test response
Source IPsNot documented

Common events

EventSent when
form_responseA videoask is completed or abandoned and all video and audio is transcoded
form_response_transcribedThe same, once all media is also transcribed, which can take 3 to 4 minutes
form_author_messageYou, the videoask's creator, send a message to a contact
form_contact_messageA contact sends a message
form_contact_taggedA tag is added to a contact
form_contact_untaggedA tag is removed from a contact

The API documentation publishes examples for the first four. The two tag events appear only in the help centre's list of toggles, so capture one before you build on it.

A form_response body, trimmed:

{
  "event_id": "7df19833701662b6414ed1765dcc626c",
  "event_type": "form_response",
  "interaction_id": "386bbc0b-bb60-4b62-82fa-d630d0d2b83b",
  "contact": {
    "contact_id": "7c6550ff-fe15-424a-918e-2c1936db8aed",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "status": "completed",
    "answers": [
      {
        "answer_id": "0f4ab93a-4914-4e4a-a33b-8466fda6147a",
        "question_id": "4d73e927-e0ef-4b73-a5da-cdda038545e1",
        "type": "poll",
        "poll_option_content": "Grapes"
      },
      {
        "answer_id": "995948a9-8a02-48f8-a018-769f0f25e7f8",
        "question_id": "a74ac70a-b207-492d-a3c8-43c8508f5c7a",
        "type": "video",
        "media_url": "https://media.videoask.com/transcoded/a8v1jwbdwwc7uq0keni9kf0c5l42vt42gp23f4ej.mp4",
        "share_url": "https://www.videoask.com/a8v1jwbdwwc7uq0keni9kf0c5l42vt42gp23f4ej",
        "transcode_status": "completed"
      }
    ]
  },
  "form": {
    "form_id": "5a1b8066-f92c-47bf-b8f2-3beb0a738122",
    "title": "Example form for documentation",
    "questions": ["..."]
  }
}

form_response_transcribed has the same shape and adds a transcription to each recorded answer. The message events add the message itself. The form object repeats the videoask's questions, so join answers to questions on question_id.

Inspect VideoAsk webhooks as they arrive. Point a webhook at a Hookdeck Inspect URL and click Send test response to see the full body and your secret header, with no account or setup required.

Setting up VideoAsk webhooks

Pick a header name, such as Authorization or X-VideoAsk-Secret, and generate a long random value, for example with openssl rand -hex 32.

In the app:

  1. Open the videoask and click Connect.
  2. Select Webhooks, then click Connect.
  3. Enter the Target URL.
  4. Toggle on the events you want.
  5. Click Add a secret and enter the Header name and Value.
  6. Adjust the idle deadline if you need to, then click Save.
  7. Click View deliveries, then Send test response.

To apply the same webhook to every new videoask, set it up once under Account & Org settings > Overview > Webhooks. Existing videoasks keep their own settings.

With the API, create or update a webhook under a tag you choose:

curl --request PUT \
  --url "https://api.videoask.com/forms/$FORM_ID/webhooks/crm-sync" \
  --header "Authorization: Bearer $VIDEOASK_ACCESS_TOKEN" \
  --header "organization-id: $VIDEOASK_ORGANIZATION_ID" \
  --header 'Content-Type: application/json' \
  --data '{
    "url": "https://example.com/webhooks/videoask",
    "event_types": ["form_response", "form_response_transcribed"],
    "headers": { "X-VideoAsk-Secret": "YOUR_LONG_RANDOM_VALUE" }
  }'

Webhooks made in the app have a random tag, which GET /forms/{form_id}/webhooks returns.

To route through Hookdeck, create a source with the VideoAsk type and use its URL as the target URL. Enter the header name in API Key Header and the exact value, including any Bearer prefix, in API Key. Hookdeck finds the header case-insensitively, compares the whole value, and accepts POST.

Securing VideoAsk webhooks

Look up your header and compare its whole value with the stored secret, in constant time.

const crypto = require('crypto');
const express = require('express');

const HEADER_NAME = (process.env.VIDEOASK_WEBHOOK_HEADER || 'x-videoask-secret').toLowerCase();
const EXPECTED = process.env.VIDEOASK_WEBHOOK_SECRET;

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/videoask', express.raw({ type: '*/*' }), (req, res) => {
  const received = req.get(HEADER_NAME);
  if (!EXPECTED || typeof received !== 'string' || !safeEqual(received, EXPECTED)) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Store event.event_id, queue the work by event.event_type, and respond.
  res.sendStatus(200);
});

The same check in Python:

import hmac
import os

HEADER_NAME = os.environ.get("VIDEOASK_WEBHOOK_HEADER", "X-VideoAsk-Secret")
EXPECTED = os.environ.get("VIDEOASK_WEBHOOK_SECRET", "")


def verify_videoask_request(headers) -> bool:
    if not EXPECTED:
        return False  # never accept when unconfigured
    received = headers.get(HEADER_NAME)  # most frameworks look headers up case-insensitively
    if not received:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(received.encode("utf-8"), EXPECTED.encode("utf-8"))

A static header authenticates the sender, not the body. It cannot detect a modified payload, and with no timestamp a captured request can be replayed and still pass. Responses can contain names, email addresses and recordings, so serve the endpoint over HTTPS only, use a long random value, and change it in the Connect tab or through the API if it might have leaked.

Make VideoAsk webhooks production-ready. Hookdeck Event Gateway checks your secret header, accepts every response as it arrives, and keeps it until your CRM or hiring workflow has processed it.

VideoAsk webhook limitations and pain points

Three retries in 35 minutes

The Problem: An outage of more than about half an hour loses responses.

Why It Happens: VideoAsk retries a failed request 3 times, after 5, 10 and 20 minutes, and documents nothing after that.

Workarounds:

  • Store the request and return a 2xx before doing any work.
  • Check View deliveries after an incident and fetch missed responses through the API.

How Hookdeck Can Help: Hookdeck stays up when your service doesn't, so VideoAsk's deliveries succeed. Hookdeck then retries delivery to your service on your schedule, for as long as you need, and you can replay any response.

Two webhooks for one interaction

The Problem: The same respondent creates two CRM records.

Why It Happens: A respondent who stays idle past the idle deadline has their answers submitted. If they then finish, VideoAsk sends another webhook with the full response. Subscribing to both form_response and form_response_transcribed also gives you two webhooks per interaction by design.

Workarounds:

  • Deduplicate on interaction_id for business logic, and on event_id for retries.
  • Subscribe only to form_response_transcribed if you need transcripts and can wait a few minutes.

How Hookdeck Can Help: Deduplication can discard repeats before they reach your service, and filters can drop the event type you don't need.

The Problem: Video links stored from a webhook stop working after a week.

Why It Happens: VideoAsk says the transcoded media URL expires after around a week. The interaction's share URL does not.

Workarounds:

  • Download recordings you need to keep when the webhook arrives.
  • Store the share URL for viewing, and make sure interactions are shareable.

How Hookdeck Can Help: Hookdeck queues the webhook until your service is ready, so the download job always starts from a fresh payload rather than a link that may already be days old.

Unclear security wording

The Problem: You look for a signature to verify and can't find the algorithm.

Why It Happens: VideoAsk's help article talks about a header "signed by VideoAsk", but the setup is a header name and a static value, and no signing scheme is documented.

Workarounds:

  • Treat the header as a shared secret and compare it whole.

How Hookdeck Can Help: Hookdeck's VideoAsk source compares the header you configure. Hookdeck answers VideoAsk with a 200 either way, but a request without the right header is recorded as failed verification and never reaches your service.

Best practices

Choose the event that matches your need

Use form_response for speed, or form_response_transcribed if you need transcripts. Subscribing to both doubles the work.

Deduplicate on event_id and interaction_id

event_id catches retries; interaction_id catches idle-then-finished responses. See our guide to webhook idempotency.

Join answers to questions

Match each answer's question_id against form.questions rather than relying on order.

Set defaults for new videoasks

Organisation default webhook settings make sure new videoasks send webhooks, with the secret header, from day one.

Acknowledge fast and process asynchronously

Store the response and return a 2xx, then download media and update other systems. See why to process webhooks asynchronously.

Conclusion

VideoAsk sends responses and messages as JSON POST requests, authenticated only by the custom header you add in the Connect tab or through the API. Compare that header in constant time, deduplicate on event_id and interaction_id, download media before the links expire, and respond quickly, because VideoAsk's three retries are over within 35 minutes.

Hookdeck Event Gateway checks the header, stores every response, and delivers it to your service with retries and replay.

Get started with Hookdeck for free and handle VideoAsk webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.