We build custom applications 5x faster and cheaper 🚀
Book a Free Consultation
Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.
Direct answer: Build a small, externally-hosted webhook receiver that validates Typeform signatures and stores or forwards events, configure OAuth (or a personal access token) to call Typeform’s REST API from your integration code, and connect that external service to your OpenClaw skill by installing/configuring the skill through ClawHub and placing tokens/credentials into the platform’s secret store. Keep the webhook server and any persistent state outside the agent runtime (agents are ephemeral); the agent/skill should focus on processing events or fetching additional Typeform data via authenticated API calls. Debug by checking webhook delivery logs, API responses, token scopes/expiration, and the skill invocation path in ClawHub and your runtime logs.
Notes: replace placeholders (YOUR_TYPEFORM_CLIENT_ID, CLIENT_SECRET, FORM\_ID, etc.). Confirm the exact signature header format and encoding with Typeform docs and adjust the verification encoding if needed.
// <b>//</b> Example: Node.js + Express webhook receiver and Typeform API calls
const express = require('express');
const fetch = require('node-fetch');
const crypto = require('crypto');
const app = express();
// <b>//</b> We need raw body for HMAC verification
app.use(express.raw({ type: 'application/json' }));
const TYPEFORM_WEBHOOK_SECRET = process.env.TYPEFORM_WEBHOOK_SECRET; // <b>//</b> configured in Typeform webhook and in your secrets manager
const TYPEFORM_BEARER = process.env.TYPEFORM_BEARER; // <b>//</b> access token (or your mechanism to fetch a token)
// <b>//</b> Raw webhook endpoint
app.post('/typeform/webhook', async (req, res) => {
const raw = req.body; // <b>//</b> Buffer
const header = req.get('Typeform-Signature') || req.get('typeform-signature') || '';
// <b>//</b> Compute HMAC SHA256 of the raw payload using your webhook secret
const hmac = crypto.createHmac('sha256', TYPEFORM_WEBHOOK_SECRET);
hmac.update(raw);
const signature = hmac.digest('base64'); // <b>//</b> confirm encoding expected by Typeform
// <b>//</b> Many providers prefix with "sha256=", adjust if required by Typeform
const expected = 'sha256=' + signature;
const safeEqual = (() => {
try {
const a = Buffer.from(header);
const b = Buffer.from(expected);
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
} catch (e) {
return false;
}
})();
if (!safeEqual) {
// <b>//</b> signature mismatch - log and return 401
console.warn('Invalid signature', header, expected);
return res.status(401).send('invalid signature');
}
// <b>//</b> Parse the JSON (we used raw middleware)
let body;
try {
body = JSON.parse(raw.toString('utf8'));
} catch (err) {
console.error('Bad JSON', err);
return res.status(400).send('bad json');
}
// <b>//</b> Acknowledge quickly
res.status(200).send('ok');
// <b>//</b> Persist or enqueue body for downstream processing.
// <b>//</b> Example: push to database or message queue so your OpenClaw skill can pick it up.
// saveEventToDb(body);
});
// <b>//</b> Example: fetch form responses from Typeform using the REST API
async function fetchResponses(formId) {
const url = `https://api.typeform.com/forms/${formId}/responses`;
const resp = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${TYPEFORM_BEARER}`,
'Accept': 'application/json'
}
});
if (!resp.ok) {
// <b>//</b> Handle errors. Log resp.status and resp.text() to debug.
throw new Error(`Typeform API error ${resp.status}: ${await resp.text()}`);
}
return resp.json();
}
// <b>//</b> Example: create/enable a webhook on a form (PUT). Confirm exact request body with Typeform docs.
async function registerWebhook(formId, webhookTag, webhookUrl, webhookSecret) {
const url = `https://api.typeform.com/forms/${formId}/webhooks/${encodeURIComponent(webhookTag)}`;
const body = {
url: webhookUrl,
enabled: true,
secret: webhookSecret
};
const resp = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${TYPEFORM_BEARER}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!resp.ok) {
throw new Error(`Failed to register webhook: ${resp.status} ${await resp.text()}`);
}
return resp.json();
}
Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.
1
The connector returns 401 Unauthorized because the OAuth token exchange is failing authentication — usually due to wrong/missing client credentials, a redirect URI mismatch, using the wrong token endpoint or auth method, an expired/used authorization code, or because the OpenClaw skill/environment variables storing the client_id/client_secret are incorrect or unset.
Check these in order:
2
The most common causes are: wrong endpoint URL or HTTP method, network/firewall or TLS blocking, Typeform webhook disabled or returning non-2xx so Typeform retries but stops, proxy or body-parsing middleware that changes the raw request (breaking HMAC verification), and a mismatched webhook secret/header name or signature algorithm used during verification.
Check these in order:
const crypto = require('crypto')
app.post('/webhook', express.raw({type:'application/json'}), (req,res)=>{
// compute HMAC on raw body
const sig = crypto.createHmac('sha256', process.env.TYPEFORM_SECRET).update(req.body).digest('base64')
if (sig !== req.header('Typeform-Signature')) return res.status(401).end()
res.status(200).end()
})
3
Direct answer: Typeform answers are nested JSON objects and arrays (answer objects, choice arrays, labels vs ids). OpenClaw’s Field Mapping fails when you target a scalar path but the payload has an array/object shape or when the mapping expression doesn’t address array indexing or property names exactly — you must inspect the real webhook JSON and use a path/transform that flattens or selects the correct element.
4
The Sync Job fails because requests to Typeform are being rate-limited and the OpenClaw job/queue retry behavior isn’t respecting the API’s backoff guidance or capacity limits: retries are too aggressive (no exponential backoff/jitter or honoring Retry-After), concurrency bursts exhaust Typeform quotas, and job timeouts/queue limits drop or fail jobs instead of pacing and requeuing reliably.
Inspect logs and Typeform responses (look for 429 and Retry-After), then:
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â