Skip to main content

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 Business and Scale plans only.

PlanWebhooksEvents
FreeNot available
StarterNot available
ProNot available
Business10 webhooksAll 19 events
Scale100 webhooksAll 19 events

Setting Up Webhooks

You can manage webhooks in two ways:

  1. Dashboard — Go to Account → API Integrations → Webhooks tab
  2. REST API — Use the /api/v1/webhooks endpoints with a Bearer token

Creating a Webhook

  1. Navigate to API Integrations in your account settings
  2. Click the Webhooks tab
  3. Click Create Webhook
  4. Enter your HTTPS endpoint URL (HTTPS is required)
  5. Select the event type you want to subscribe to
  6. Optionally filter to a specific form
  7. Click Create

Important: When you create a webhook, a signing secret is displayed once. Copy and save it securely — you’ll need it to verify webhook signatures. It cannot be retrieved again.

Event Types

Uplup supports 19 webhook events organized into 5 categories. Each webhook subscribes to one event type.

Form Lifecycle

EventDescription
form.createdA new form or quiz was created
form.updatedForm settings were modified
form.deletedA form was deleted
form.publishedA form was published (made live)
form.unpublishedA form was taken offline
form.clonedA form was duplicated

Submissions

EventDescription
submission.createdA new form response was received
submission.completedA multi-step submission was completed (reserved)
submission.deletedA submission was deleted

Quiz

EventDescription
quiz.completedA quiz was scored and results generated
quiz.passedA quiz taker met the passing score threshold
quiz.failedA quiz taker did not meet the passing score
quiz.timer_expiredA quiz timer ran out before the taker could finish

Fields

EventDescription
field.createdA field was added to a form
field.updatedA field’s properties were modified
field.deletedA field was removed from a form

Responses

EventDescription
response.startedA user began filling out a form (first interaction per session)
response.page_completedA user completed a form page (with field data for that page)
response.abandonedA user left the form without completing it

Payload Format

Every webhook delivery sends a JSON payload with this structure:

{
  "event": "submission.created",
  "webhook_id": 42,
  "form_id": "abc12345",
  "timestamp": "2026-03-09T12:00:00Z",
  "data": {
    "submission_id": "sub_789",
    "form_id": "abc12345",
    "fields": {
      "email": "user@example.com",
      "name": "Jane Smith"
    }
  }
}

Request Headers

Each webhook request includes these headers:

HeaderDescription
Content-Typeapplication/json
X-Uplup-SignatureHMAC-SHA256 hex digest of the request body
X-Webhook-EventThe event type (e.g., submission.created)
X-Webhook-IDUnique ID of the webhook subscription
User-AgentUplup-Webhooks/1.0

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: 5 seconds — your endpoint must respond within 5 seconds
  • Success: Any 2xx HTTP status code is considered successful delivery
  • HTTPS required: Webhook URLs must use HTTPS (HTTP is rejected)
  • No automatic retries: Failed deliveries are logged but not automatically retried. Use the “Send Test” button to re-test
  • Delivery logs: The last 20 deliveries per webhook are stored with status code, response time, and error details

Testing Webhooks

You can test your webhook endpoint directly from the dashboard:

  1. Find your webhook in the Webhooks tab
  2. Click Send Test
  3. A sample event payload will be sent to your URL
  4. The result shows the HTTP status code and response time

Test events are rate-limited to 5 per minute per webhook. Test deliveries appear in the delivery log just like real events.

Managing Webhooks

Pausing a Webhook

Toggle the switch next to any webhook to pause or resume it. Paused webhooks do not receive any events.

Deleting a Webhook

Click the delete button and confirm to remove a webhook. Deleted webhooks stop receiving events immediately.

Filtering by Form

When creating a webhook, you can optionally select a specific form. The webhook will only fire for events related to that form. Leave the filter empty to receive events for all forms.

Security Best Practices

  • Always verify signatures — Never process a webhook payload without verifying the X-Uplup-Signature header
  • 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 webhook_id and timestamp to detect duplicate deliveries

FAQ

Which plans support webhooks?

Webhooks are available on Business (10 webhooks) and Scale (100 webhooks) plans. Free, Starter, and Pro plans do not include webhook access.

Can I subscribe to multiple events?

Each webhook subscribes to one event type. To receive multiple event types, create separate webhooks — one per event. This gives you fine-grained control over which events go to which endpoints.

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 delivery logs to diagnose issues.

Can I filter webhooks to specific forms?

Yes. When creating a webhook, you can select a specific form to filter events. Only events from that form 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 error. Uplup does not retry failed deliveries. Monitor your delivery logs regularly to catch any issues.