Webhooks | Real-time Event Notifications
What are Webhooks?
Webhooks let you receive real-time HTTP notifications when events happen in your Uplup forms and quizzes. Instead of polling the API, webhooks push data to your server the moment something occurs — a submission is received, a quiz is completed, a form is published, and more.
Uplup webhooks follow the same industry-standard patterns used by Stripe, GitHub, and Typeform: HTTPS delivery, HMAC-SHA256 signatures for verification, and structured JSON payloads.

Plan Requirements
Webhooks are available on the Business plan and above.
| Plan | Webhooks |
|---|---|
| Free | Not available |
| Pro | Not available |
| Business | All 19 events, per-form filtering, signing secret |
Setting Up Webhooks
- Open your brand and go to the API Integrations page
- Open the Webhooks tab (under the Developer section)
- Click Create Webhook
- Enter your HTTPS endpoint URL (HTTPS is required, HTTP is rejected)
- Select the events you want to subscribe to (one webhook can subscribe to many events — by default, all events are selected)
- Optionally filter to specific forms (leave empty to receive events for all forms in the workspace)
- Click Create Webhook
Important: When you create a webhook, the signing secret is shown once after creation. Copy and save it securely — you will need it to verify webhook signatures. You can regenerate it at any time from the webhook detail page, but doing so invalidates the previous secret.
Event Types
Uplup supports 19 webhook events organized into 5 categories. A single webhook can subscribe to any number of these events.

Form Lifecycle
| Event | Description |
|---|---|
form.created |
A new form or quiz is created |
form.updated |
Form settings are modified |
form.deleted |
A form or quiz is deleted |
form.published |
A form is published (made live) |
form.unpublished |
A form is taken offline |
form.cloned |
A form is duplicated |
Submissions
| Event | Description |
|---|---|
submission.created |
A new form response is received |
submission.completed |
A multi-step submission is completed |
submission.deleted |
A submission is deleted |
Quiz
| Event | Description |
|---|---|
quiz.completed |
A quiz is scored and completed |
quiz.passed |
A quiz is completed with a passing score |
quiz.failed |
A quiz is completed with a failing score |
quiz.timer_expired |
A quiz timer ran out before the taker could finish |
Fields
| Event | Description |
|---|---|
field.created |
A field is added to a form |
field.updated |
A field is modified |
field.deleted |
A field is removed |
Responses
| Event | Description |
|---|---|
response.started |
A user begins filling out a form |
response.page_completed |
A user completes a form page (with field data for that page) |
response.abandoned |
A user leaves without completing the form |
Payload Format
Every webhook delivery sends a JSON payload with this structure:
{
"event": "submission.created",
"form_id": "abc12345",
"timestamp": 1741521600,
"data": {
"submission_id": "sub_789",
"fields": {
"email": "user@example.com",
"name": "Jane Smith"
}
}
}
timestamp is a Unix epoch in seconds. The data object varies by event type. The webhook subscription ID is delivered in the X-Webhook-Id header (see below).
Request Headers
Each webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type |
application/json |
X-Uplup-Signature |
HMAC-SHA256 hex digest of the request body, signed with your webhook’s secret |
X-Webhook-Event |
The event type (e.g., submission.created) |
X-Webhook-Id |
The unique ID of the webhook subscription that fired |
Verifying Webhook Signatures
Always verify the X-Uplup-Signature header to confirm the request came from Uplup. The signature is an HMAC-SHA256 hex digest of the raw request body, using your webhook’s signing secret as the key.
JavaScript (Node.js)
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-uplup-signature'];
if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Received:', event.event);
res.status(200).send('OK');
});
Python
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Uplup-Signature', '')
if not verify_webhook(request.data, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
print(f"Received: {event['event']}")
return 'OK', 200
PHP
<?php
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_UPLUP_SIGNATURE'] ?? '';
$secret = getenv('WEBHOOK_SECRET');
$expected = hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($body, true);
error_log('Received: ' . $event['event']);
http_response_code(200);
echo 'OK';
Delivery Policy
- Timeout: 10 seconds — your endpoint must respond within 10 seconds
- Success: Any 2xx HTTP status code is considered a successful delivery
- HTTPS required: Webhook URLs must use HTTPS (HTTP is rejected at create time)
- No automatic retries: Failed deliveries are logged but not automatically retried. Use the Send Test button to re-test, or check the delivery log to diagnose issues
- Delivery logs: Each delivery is logged with the event type, HTTP status code, and timestamp, viewable in the webhook detail page
Testing Webhooks
You can test your webhook endpoint directly from the dashboard:
- Open the webhook in the Webhooks tab
- Click Send Test
- A sample event payload will be sent to your URL
- The result shows the HTTP status code and response time
Test deliveries appear in the delivery log just like real events.
Managing Webhooks
Pausing a Webhook
Toggle the switch next to any webhook in the list view to pause or resume it. Paused webhooks do not receive any events.
Editing a Webhook
Open a webhook to change the URL, the subscribed events, the form filter, or to regenerate the signing secret.
Deleting a Webhook
Click Delete on the webhook detail page and confirm. Deleted webhooks stop receiving events immediately.
Filtering by Form
When creating or editing a webhook, you can filter to one or more specific forms. The webhook will only fire for events related to those forms. Leave the filter empty to receive events for every form in the workspace.
Security Best Practices
- Always verify signatures — Never process a webhook payload without verifying the
X-Uplup-Signatureheader - Use HTTPS — All webhook URLs must use HTTPS. Uplup rejects HTTP endpoints
- Store secrets securely — Keep your signing secret in environment variables, never in source code
- Respond quickly — Process webhooks asynchronously. Return a 200 immediately, then handle the event in a background job
- Handle duplicates — Use the
X-Webhook-Idheader andtimestampfield to detect duplicate deliveries
FAQ
Which plans support webhooks?
Webhooks are available on the Business plan and above. Free and Pro plans do not include webhook access.
Can a single webhook subscribe to multiple events?
Yes. When you create or edit a webhook, you can select any number of event types. By default, all 19 events are selected.
Are webhook deliveries retried?
No. Failed deliveries are logged with error details, but not automatically retried. You can re-test manually using the Send Test button, or check the delivery log to diagnose issues.
Can I filter webhooks to specific forms?
Yes. When creating or editing a webhook, you can select one or more specific forms to filter on. Only events from those forms will trigger the webhook. Leave the filter empty to receive events for all forms in your workspace.
What happens if my server is down?
The delivery will fail and be logged with the HTTP status code or error. Uplup does not retry failed deliveries, so you should monitor your delivery log to catch any issues.
What is the request timeout?
Uplup waits up to 10 seconds for your endpoint to respond. Any longer counts as a failed delivery.
