Guide to VideoAsk Webhooks: Features and Best Practices
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
| Feature | Details |
|---|---|
| Availability | Paid plans |
| Configuration | Connect tab > Webhooks, organisation default webhook settings, or PUT /forms/{form_ |
| Method and format | POST, JSON |
| Event field | event_type |
| Authentication | A secret header you set (Add a secret, or headers in the API). No signature |
| Success | Any 2xx |
| Retries | 3, after 5, 10 and 20 minutes |
| Idle deadline | 60 minutes by default, 15 to 240 configurable |
| Testing | View deliveries > Send test response |
| Source IPs | Not documented |
Common events
| Event | Sent when |
|---|---|
form_response | A videoask is completed or abandoned and all video and audio is transcoded |
form_ | The same, once all media is also transcribed, which can take 3 to 4 minutes |
form_author_message | You, the videoask's creator, send a message to a contact |
form_contact_message | A contact sends a message |
form_contact_tagged | A tag is added to a contact |
form_contact_untagged | A 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_ 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:
- Open the videoask and click Connect.
- Select Webhooks, then click Connect.
- Enter the Target URL.
- Toggle on the events you want.
- Click Add a secret and enter the Header name and Value.
- Adjust the idle deadline if you need to, then click Save.
- 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_ 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
2xxbefore 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_ also gives you two webhooks per interaction by design.
Workarounds:
- Deduplicate on
interaction_idfor business logic, and onevent_idfor retries. - Subscribe only to
form_if you need transcripts and can wait a few minutes.response_ transcribed
How Hookdeck Can Help: Deduplication can discard repeats before they reach your service, and filters can drop the event type you don't need.
Media links expire
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_ 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.