The custom_webhook skill
What the skill does
The 13 first-party AskVault skills cover most patterns: lead capture, escalation, scheduling, order status, sentiment routing. The 14th skill, custom_webhook, is the escape hatch for everything else.
When trigger phrases match, the skill calls a webhook you configure with the conversation context. Your endpoint does whatever it wants: write to a database, trigger a workflow, call an API we don't have a native integration for, fire a Slack message with custom formatting.
The skill is on Business and above. Business+
Real use cases
Five patterns we see most often:
- CRM other than HubSpot or Salesforce. Pipedrive, Close, Copper, Insightly. Webhook hits your CRM's API.
- Custom internal systems. Your home-grown ticketing, inventory, or returns systems. Webhook bridges to your backend.
- Real-time order status from a non-Shopify, non-WooCommerce store. Magento, BigCommerce, custom Rails store. Webhook calls your store's API.
- Slack notifications with custom formatting. Beyond AskVault's built-in Slack alerts. Webhook fires to Slack's webhook with your specific message template.
- Trigger a Zapier or Make workflow. Webhook is the universal hand-off to any "no-code" automation product.
Setup
Configure under AI Agents > Skills > custom_webhook:
- Enable the skill.
- Webhook URL. Where AskVault POSTs when the skill fires.
- Authentication. Either a static Bearer token in the Authorization header, or HMAC signature of the request body. We recommend HMAC for security.
- Trigger phrases. What customer messages trigger the webhook. Example: "create a ticket", "log this to Pipedrive", "send this to my team".
- Request schema. What fields go in the POST body. Default schema below; customize per your endpoint's needs.
Test the webhook configuration under Skills > custom_webhook > Test. AskVault fires a sample request to your URL and shows you the response.
Default request schema
What AskVault POSTs:
{ "event": "custom_webhook.triggered", "skill_id": "custom_webhook", "workspace_id": "wt_xxx", "conversation_id": "conv_xxx", "channel": "widget", "customer": { "email": "verified@example.com", "name": "Jane Doe", "phone": "+1 555 0100", "user_id": "your-app-user-id-42" }, "trigger_phrase": "create a ticket", "recent_messages": [ {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."} ], "metadata": { "source_url": "https://acme.co/pricing", "request_id": "req_xxx" }}Field-by-field customization under Skills > custom_webhook > Request Schema. You can rename fields, omit some, add static fields, or compute derived fields with simple JSONPath-like expressions.
Response handling
Your webhook returns a response. AskVault uses it to compose the bot's reply to the customer:
{ "reply": "I've logged ticket #1234. We'll be in touch within 4 hours.", "metadata": { "ticket_id": "1234", "queue": "billing" }}reply(required). What the bot says to the customer. Markdown supported.metadata(optional). Free-form fields stored on the conversation for later retrieval or analytics.
Return HTTP 200 with this JSON body. Status >= 400 makes the bot say something generic like "I tried to do that but something failed; let me get a human."
Security
HMAC signatures are strongly recommended. Configuration:
- Set a shared secret under Skills > custom_webhook > HMAC Secret.
- AskVault computes
HMAC-SHA256(request_body, secret)and includes it in theX-AskVault-Signatureheader. - Your endpoint recomputes the HMAC and compares. If mismatch, reject with 401.
Code example (Node.js):
import { createHmac, timingSafeEqual } from 'crypto';
function verifyAskVaultWebhook(body, signature, secret) { const expected = createHmac('sha256', secret).update(body).digest('hex'); return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));}Without HMAC, anyone who guesses your webhook URL could fire fake requests to your endpoint. HMAC closes this.
Policy bounds
The skill respects two policy bounds:
- Rate limit. Maximum invocations per minute per workspace. Default 10. Tunable under Skills > custom_webhook > Rate Limit.
- Trigger budget. Maximum invocations per conversation. Default 3. Prevents the bot from looping if your webhook keeps returning ambiguous responses.
Hitting either bound surfaces a polite fallback to the customer and a Slack alert to your team.
Latency
End-to-end latency depends on your webhook's response time:
- AskVault's overhead: about 200 ms.
- Your webhook: as fast as it returns.
Total. If your webhook takes 2 seconds, the bot's reply takes about 2.2 seconds. Keep webhook latency under 3 seconds for good UX. Anything slower and the bot pre-sends a "working on it..." message, then replaces it with the real reply once your webhook returns.
For very slow operations (10+ seconds), use a fire-and-forget pattern: webhook returns immediately, completes the work async, and follows up via the conversations API.
Limits
- Plan availability. Business and above.
- Request body size. Up to 64 KB. Conversations longer than this get the most recent 64 KB.
- Response timeout. 10 seconds. Beyond that, AskVault assumes failure.
- Per-conversation invocations. Default 3, hard max 10.
Common pitfalls
Webhook never fires. Trigger phrase doesn't match. Test with exact phrases under Skills > custom_webhook > Test.
HMAC verification fails. Secret mismatch between AskVault and your endpoint. Re-copy the secret from the dashboard and update your endpoint.
Response is ignored. Your endpoint returned 200 but with the wrong JSON shape. Must include reply field at minimum.
Bot says generic error. Your webhook returned >= 400. Check your endpoint's logs and AskVault's webhook delivery history under Dashboard > Webhooks > Deliveries.
Per-conversation invocation cap hit. Customer keeps triggering the same intent. Either loosen the cap, or refine your trigger phrases to be more selective.
FAQ
Can the bot call multiple webhooks per conversation?
Yes, sequentially. The skill fires when any of its trigger phrases match. You can configure multiple custom_webhook skill instances with different webhook URLs and trigger lists.
Does HMAC use the request timestamp?
Not by default; only the body. For replay-attack protection, include a timestamp in the request body and reject old timestamps on your endpoint side. AskVault includes a millisecond-precision timestamp in metadata.fired_at.
Can I return JSON other than the standard schema?
Yes. Configure response field mapping under Skills > custom_webhook > Response Mapping. Your endpoint returns whatever shape; mapping rules pull out the bot's reply text.
Is there an idempotency mechanism?
Each call includes a unique request_id in the body. Your endpoint can dedupe on this if needed. AskVault doesn't retry on success; it does retry once on 5xx failures.
Can the skill fire on every message instead of by trigger phrase?
Yes. Set the trigger to a wildcard match under Skills > custom_webhook > Trigger Mode = always. The webhook fires on every customer message in the conversation. Use sparingly; rate limits apply.
Related guides
- Webhooks reference
- Ticketing router skill
- Escalate to human skill
- HubSpot integration setup
- Stripe integration setup