Skip to content

Beepclick / Take your links further

Build with Beepclick.

Links, conversion events and signed webhooks.

Getting started

Create a scoped key in your workspace. Keep sk_ keys on your server. Pro: 60 requests/minute. Elite: 600. read allows GET, write allows link mutations, events allows server-side event ingestion.

Manage keysOpenAPI 3.1 (JSON)

Supported endpoints: GET/POST /v1/links, GET/PATCH/DELETE /v1/links/{id}, GET /v1/links/{id}/stats, POST /v1/events, GET /v1/conversions/analytics. Other workspace routes require a session.

Pagination uses limit and nextCursor. Mutations accept Idempotency-Key for 24 hours. Reusing it with a different request returns 409. A 429 response includes Retry-After. Errors use application/problem+json and a stable code.

JavaScript

const response = await fetch('https://beepclick.app/v1/links', {
  method: 'POST',
  headers: {Authorization: 'Bearer ' + process.env.BEEP_API_KEY,
    'Content-Type': 'application/json', 'Idempotency-Key': 'order-123-link'},
  body: JSON.stringify({destination: 'https://example.com/offer'})
});
console.log(await response.json());

Python

import os, requests
response = requests.post('https://beepclick.app/v1/links',
  headers={'Authorization': 'Bearer ' + os.environ['BEEP_API_KEY'],
           'Idempotency-Key': 'order-123-link'},
  json={'destination': 'https://example.com/offer'})
response.raise_for_status()
print(response.json())

PHP

<?php
$ch = curl_init('https://beepclick.app/v1/links');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer '.getenv('BEEP_API_KEY'),
    'Content-Type: application/json', 'Idempotency-Key: order-123-link'],
  CURLOPT_POSTFIELDS => json_encode(['destination' => 'https://example.com/offer'])]);
echo curl_exec($ch);
curl_close($ch);

beep.js & consent

<script>window.beep=window.beep||function(){(window.beep.q=window.beep.q||[]).push(arguments)};</script>
<script defer src="https://beepclick.app/beep.js" data-key="pk_YOUR_PUBLIC_KEY"></script>
// Only after explicit consent from your CMP:
beep('consent', true);
beep('track', 'sale', {value: 49, currency: 'EUR'});
// Withdrawal:
beep('consent', false);

Public keys only collect events from exact allowed origins. The default mode records anonymous page views without cookies. Add data-cookieless="true" to force that mode. Consent is never inferred. lead, sale, signup, subscription and custom:name require consent. Enable tracking separately for each link.

{
  "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
  "name": "sale",
  "cid": "SIGNED_BEEP_CID",
  "visitor": "your-consented-visitor-id",
  "value": 49,
  "currency": "EUR",
  "consent": true
}

Send this JSON to POST /v1/events with an events-scoped server key. Reuse eventId when retrying. Amounts are grouped by currency and never summed across currencies.

Verify webhook signatures

Beepclick-Signature contains t=UNIX_SECONDS,v1=HEX_HMAC. Compute HMAC-SHA256 with your whsec_ secret over timestamp + '.' + the exact request body. Compare in constant time, reject timestamps older than 5 minutes, and deduplicate the event id. Delivery is at least once, with eight attempts and exponential backoff. 3xx responses are not followed.

import {createHmac, timingSafeEqual} from 'node:crypto';
const expected = createHmac('sha256', process.env.BEEP_WEBHOOK_SECRET)
  .update(timestamp + '.' + rawBody).digest();
const received = Buffer.from(signatureHex, 'hex');
const valid = received.length === expected.length &&
  timingSafeEqual(received, expected) &&
  Math.abs(Date.now()/1000 - Number(timestamp)) < 300;