Webhooks

Receive real-time HTTP notifications when events occur in your account

Overview

GrowQR Webhooks deliver real-time HTTP POST notifications to your server whenever key events happen in your workspace — a link is clicked, a new URL is created, a campaign is updated, a conversion is recorded, or a landing page is published. Instead of polling the API to check for changes, your systems are notified the instant something happens.

Webhooks are the backbone of event-driven integrations. Use them to sync data with your CRM, trigger Slack notifications, update analytics dashboards, kick off automation workflows, or feed events into a data warehouse — all in real time.

What Problem It Solves

API polling is wasteful and slow. If you check the API every 60 seconds for new clicks, you're making 1,440 requests per day — most returning no new data — and you still have up to 60 seconds of latency. At scale, polling burns API quota, increases server costs, and delivers a poor real-time experience.

Webhooks flip the model: GrowQR pushes events to you. You receive data within seconds of the event, with zero wasted requests. Your integration code becomes simpler (just an HTTP endpoint that accepts POST requests) and your data stays fresh.

How It Works

When an event occurs in your workspace, GrowQR's event system:

  1. Serializes the event into a JSON payload with a consistent schema.
  2. Signs the payload using HMAC-SHA256 with your webhook's secret key, placing the signature in the X-GrowQR-Signature header.
  3. Delivers the payload via HTTP POST to your configured endpoint URL.
  4. Handles failures with automatic retries using exponential backoff (3 attempts over 1 hour).

Available Events

EventTrigger
url.createdA new short link is created
url.clickedA short link receives a click
url.updatedA short link's properties are modified
url.deletedA short link is deleted
campaign.createdA new campaign is created
campaign.updatedA campaign's settings are changed
campaign.deletedA campaign is deleted
conversion.createdA new conversion event is tracked
landing_page.publishedA landing page is published or updated

You can subscribe to individual events or select All Events to receive everything.

Step-by-Step Usage

Creating a Webhook

  1. Navigate to Dashboard → Settings → Webhooks.
  2. Click Create Webhook.
  3. Enter the Endpoint URL — this must be a publicly accessible HTTPS URL that accepts POST requests. For example: https://api.yourcompany.com/webhooks/growqr.
  4. Select the Events you want to receive (or choose All Events).
  5. Click Create. GrowQR generates a signing secret displayed once — copy and store it securely.
Webhook Created
──────────────────────────────────────────
Endpoint:   https://api.yourcompany.com/webhooks/growqr
Events:     url.created, url.clicked, conversion.created
Secret:     whsec_a1b2c3d4e5f6...  (shown once)
Status:     Active

Payload Format

Every webhook delivery sends a JSON payload with the following structure:

{
  "id": "evt_7f3a9b2c4d5e6f",
  "event": "url.clicked",
  "created_at": "2026-03-05T14:23:01Z",
  "data": {
    "link_id": "link_abc123",
    "short_url": "https://go.yourcompany.com/demo",
    "destination_url": "https://yourcompany.com/product/demo",
    "click": {
      "timestamp": "2026-03-05T14:23:01Z",
      "ip_country": "US",
      "ip_city": "San Francisco",
      "device": "mobile",
      "browser": "Chrome",
      "os": "iOS",
      "referrer": "https://twitter.com"
    }
  }
}

The data object varies by event type. URL events include link details, campaign events include campaign metadata, and conversion events include conversion value and attribution data.

HMAC Signature Verification

Every webhook request includes an X-GrowQR-Signature header containing an HMAC-SHA256 signature of the raw request body. Always verify this signature to confirm the request genuinely came from GrowQR and wasn't tampered with.

Node.js verification example:

const crypto = require('crypto');
 
function verifyWebhookSignature(req, secret) {
  const signature = req.headers['x-growqr-signature'];
  if (!signature) return false;
 
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(req.rawBody)
    .digest('hex');
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}
 
// Express.js middleware
app.post('/webhooks/growqr', express.raw({ type: 'application/json' }), (req, res) => {
  const isValid = verifyWebhookSignature(
    { headers: req.headers, rawBody: req.body },
    process.env.GROWQR_WEBHOOK_SECRET
  );
 
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
 
  const event = JSON.parse(req.body);
 
  switch (event.event) {
    case 'url.clicked':
      handleClick(event.data);
      break;
    case 'url.created':
      handleNewLink(event.data);
      break;
    case 'conversion.created':
      handleConversion(event.data);
      break;
    default:
      console.log(`Unhandled event: ${event.event}`);
  }
 
  res.status(200).json({ received: true });
});

Key points:

  • Use crypto.timingSafeEqual to prevent timing attacks.
  • Parse the raw body for signature computation, not a parsed JSON object.
  • Return 200 promptly. Process events asynchronously if your handler is slow.

Retry Mechanism

If your endpoint returns a non-2xx status code or fails to respond within 30 seconds, GrowQR retries the delivery automatically with exponential backoff:

AttemptDelay After Failure
1st retry~30 seconds
2nd retry~2 minutes
3rd retry~8 minutes
4th retry~32 minutes
5th retry~2 hours

Retries use an exponential backoff formula (base delay × 2^retryCount) with the system checking for pending retries every 30 seconds. After five failed attempts (configurable per webhook), the delivery is marked as Failed in the delivery log.

The delivery log shows retry status for each event, including the current retry count and the time of the next scheduled retry. You can also manually redeliver any failed event from the dashboard.

If a webhook endpoint fails consistently (10 consecutive failures), GrowQR automatically disables the webhook and sends an email notification to workspace Admins and the Owner.

Delivery History

  1. Navigate to Dashboard → Settings → Webhooks.
  2. Click on a webhook to open its detail view.
  3. The Deliveries tab shows a log of every delivery attempt with:
    • Timestamp
    • Event type
    • HTTP status code returned by your endpoint
    • Response time
    • Payload (expandable)

Use the delivery log to debug integration issues, verify payloads, and manually redeliver failed events.

Testing Webhooks

Before wiring up a production endpoint, test your webhook configuration:

  1. Use a service like webhook.site or RequestBin to get a temporary endpoint URL.
  2. Create a webhook in GrowQR pointed at that temporary URL.
  3. Trigger an event (e.g., create a new short link).
  4. Check the testing service to see the delivered payload and headers.
  5. Verify the X-GrowQR-Signature header against your signing secret.

Alternatively, use the Send Test Event button in the webhook detail view. GrowQR sends a sample payload for each subscribed event so you can verify your endpoint handles them correctly.

Best Practices

  • Always verify signatures. Never trust a webhook payload without validating the HMAC signature. An unverified endpoint is vulnerable to spoofed events.
  • Return 200 quickly and process asynchronously. If your event handler involves database writes, API calls, or heavy computation, enqueue the work and return 200 immediately. A slow response triggers unnecessary retries.
  • Implement idempotency. Webhook deliveries may be duplicated due to retries or network issues. Use the id field in the payload to deduplicate events. Store processed event IDs and skip any you've already handled.
  • Subscribe only to the events you need. Receiving url.clicked for a high-traffic link can generate thousands of deliveries per hour. If you only need url.created, don't subscribe to click events.
  • Use HTTPS endpoints exclusively. GrowQR only delivers webhooks to HTTPS URLs. Ensure your SSL certificate is valid and not self-signed.
  • Monitor delivery success rates in the Webhooks dashboard. A sudden drop in success rate indicates an endpoint issue. Set up alerting on your endpoint's error rate as a backup.
  • Rotate your webhook secret periodically. Navigate to the webhook settings and click Regenerate Secret. Update your verification code with the new secret. The old secret remains valid for 24 hours to avoid dropped deliveries during rotation.

Example Workflows

Real-Time Slack Notifications

  1. Create a webhook subscribing to url.created and campaign.created.
  2. Point the endpoint to a small serverless function (AWS Lambda, Vercel, or Cloudflare Workers).
  3. The function parses the event, formats a message ("New link created: go.co.com/launch by jane@co.com"), and posts it to a Slack channel via the Slack Incoming Webhooks API.
  4. Your team sees every new link and campaign the moment it's created.

CRM Sync on Conversion

  1. Subscribe to conversion.created events.
  2. When a conversion fires, your endpoint looks up the visitor's email (passed via UTM or landing page form) in your CRM.
  3. The CRM contact is updated with a "Converted via GrowQR" tag and the conversion value.
  4. Sales reps see real-time conversion data without leaving the CRM.

Data Warehouse Ingestion

  1. Subscribe to All Events.
  2. Point the webhook to a lightweight ingestion endpoint that writes raw payloads to a message queue (Kafka, SQS, Pub/Sub).
  3. A downstream consumer reads from the queue, transforms the data, and loads it into your data warehouse (BigQuery, Snowflake, Redshift).
  4. Analytics teams query GrowQR events alongside other marketing data for unified reporting.