Dynamic Links

Create redirect URLs on-the-fly, without storing any record in Hozip. The destination is signed into the URL itself.

How it works

A dynamic link is a short URL whose destination is not stored in Hozip's database. Instead, the destination URL is encrypted into a signature query parameter. When a visitor opens the link, Hozip decrypts the signature, applies your group's settings (monetization, access rules, analytics), and redirects the visitor — all without any prior API call from your side.

Dynamic link URL shape:

https://DOMAIN/e/GROUP_ID?signature=TOKEN

Because no database record is written, you can generate millions of unique destination URLs instantly, entirely on your own infrastructure.

Dynamic links inherit all settings of the group they belong to: monetization, geographic rules, analytics config, and UTM parameters. Configure those once in the dashboard and every dynamic link under that group benefits automatically.

Prerequisites

  1. A group ID — open Dashboard → Groups, pick or create a group, and copy its ID.
  2. The group key — on the same Group Settings page, copy the Key field next to the ID. This is a base64url-encoded 32-byte key unique to your group.
  3. A domain — any domain listed under Enabled Domains works. Use the domain hostname (e.g. hozip.link).
Keep your group key secret. Anyone who has it can generate valid dynamic links for that group. Treat it like an API key — do not embed it in client-side or public code.

Signature format

The signature token is produced in three steps:

1. Build the JSON payload

{
  "destination_url": "https://example.com/landing?ref=campaign",
  "back_param": "utm_source=hozip&utm_medium=dynamic"
}
Field Type Required Description
destination_url string Required The URL the visitor will be redirected to. Must start with https:// (plain http:// is automatically upgraded). Max 2048 characters.
back_param string No Additional query-string parameters to append to destination_url at redirect time. Provide without a leading ? or & — Hozip adds the right separator automatically. Example: "ref=hozip&click_id=ABC123".

2. Encrypt the payload

Serialize the payload as compact JSON (no extra whitespace), then encrypt it using AES-256-GCM with an empty AAD (b""):

key       = base64url_decode(GROUP_KEY)   # 32 bytes
iv        = random_bytes(12)              # 12-byte nonce (GCM standard)
plaintext = json.dumps(payload).encode()  # UTF-8
aad       = b""                           # empty additional authenticated data

ciphertext_and_tag = AES_256_GCM_encrypt(key, iv, plaintext, aad)
# (the 16-byte GCM authentication tag is appended to the ciphertext)

3. Encode the token

Concatenate and base64url-encode without padding:

raw   = b"\x01" + iv + ciphertext_and_tag
token = base64url_no_padding_encode(raw)
Bytes Length Description
0x01 1 byte Version marker — always 0x01.
IV 12 bytes Random nonce. Must be unique per token.
Ciphertext + tag variable + 16 bytes AES-GCM ciphertext followed by the 16-byte authentication tag.

Base64url encoding uses the URL-safe alphabet (- and _ instead of + and /) and omits = padding.

Building the final URL

https://DOMAIN/e/GROUP_ID?signature=TOKEN

URL-encode the TOKEN when inserting it into the query string.

Example:

https://hozip.link/e/018e1b2c-0000-7f8a-9b0c-1d2e3f4a5b6c?signature=AQoK...

Code examples

Python

import base64
import json
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from urllib.parse import quote


GROUP_KEY_B64U = "YOUR_GROUP_KEY"   # from Dashboard → Group Settings → Key
GROUP_ID       = "YOUR_GROUP_ID"    # from Dashboard → Group Settings → ID
DOMAIN         = "hozip.link"       # any enabled domain


def b64u_decode(s: str) -> bytes:
    pad = "=" * (-len(s) % 4)
    return base64.urlsafe_b64decode(s + pad)


def b64u_encode(b: bytes) -> str:
    return base64.urlsafe_b64encode(b).rstrip(b"=").decode()


def build_dynamic_link(destination_url: str, back_param: str = "") -> str:
    key = b64u_decode(GROUP_KEY_B64U)
    payload = {"destination_url": destination_url}
    if back_param:
        payload["back_param"] = back_param

    iv = os.urandom(12)
    aead = AESGCM(key)
    plaintext = json.dumps(payload, separators=(",", ":")).encode()
    ct_and_tag = aead.encrypt(iv, plaintext, b"")  # empty AAD

    token = b64u_encode(b"\x01" + iv + ct_and_tag)
    return f"https://{DOMAIN}/e/{GROUP_ID}?signature={quote(token)}"


# --- usage ---
url = build_dynamic_link(
    destination_url="https://example.com/landing",
    back_param="utm_source=hozip&utm_medium=dynamic",
)
print(url)

Node.js

import crypto from "crypto";
const GROUP_KEY_B64U = "YOUR_GROUP_KEY"; // Dashboard → Group Settings → Key
const GROUP_ID       = "YOUR_GROUP_ID";  // Dashboard → Group Settings → ID
const DOMAIN         = "hozip.link";


function b64uDecode(s) {
  const pad = "=".repeat((4 - (s.length % 4)) % 4);
  return Buffer.from(s.replace(/-/g, "+").replace(/_/g, "/") + pad, "base64");
}

function b64uEncode(buf) {
  return buf.toString("base64")
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=/g, "");
}

function buildDynamicLink(destinationUrl, backParam = "") {
  const key = b64uDecode(GROUP_KEY_B64U);
  const payload = { destination_url: destinationUrl };
  if (backParam) payload.back_param = backParam;

  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
  cipher.setAAD(Buffer.alloc(0)); // empty AAD

  const plaintext = Buffer.from(JSON.stringify(payload));
  const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
  const tag = cipher.getAuthTag(); // 16 bytes

  const raw = Buffer.concat([Buffer.from([0x01]), iv, ct, tag]);
  const token = b64uEncode(raw);
  return `https://${DOMAIN}/e/${GROUP_ID}?signature=${encodeURIComponent(token)}`;
}

// --- usage ---
const url = buildDynamicLink(
  "https://example.com/landing",
  "utm_source=hozip&utm_medium=dynamic"
);
console.log(url);

PHP

<?php
function b64uDecode(string $s): string {
    $pad = str_repeat("=", (4 - strlen($s) % 4) % 4);
    return base64_decode(strtr($s, "-_", "+/") . $pad);
}

function b64uEncode(string $bin): string {
    return rtrim(strtr(base64_encode($bin), "+/", "-_"), "=");
}

function buildDynamicLink(
    string $groupKeyB64u,
    string $groupId,
    string $domain,
    string $destinationUrl,
    string $backParam = ""
): string {
    $key     = b64uDecode($groupKeyB64u);
    $iv      = random_bytes(12);
    $payload = ["destination_url" => $destinationUrl];
    if ($backParam !== "") $payload["back_param"] = $backParam;

    $plaintext = json_encode($payload, JSON_UNESCAPED_SLASHES);
    $tag       = "";
    $ct        = openssl_encrypt(
        $plaintext, "aes-256-gcm", $key,
        OPENSSL_RAW_DATA, $iv, $tag, "", 16
    );

    $raw   = "\x01" . $iv . $ct . $tag;
    $token = b64uEncode($raw);
    return "https://{$domain}/e/{$groupId}?signature=" . urlencode($token);
}

// --- usage ---
echo buildDynamicLink(
    groupKeyB64u: "YOUR_GROUP_KEY",
    groupId:      "YOUR_GROUP_ID",
    domain:       "hozip.link",
    destinationUrl: "https://example.com/landing",
    backParam:    "utm_source=hozip&utm_medium=dynamic"
);

Behaviour at redirect time

When a visitor opens a dynamic link, Hozip:

  1. Decrypts and verifies the signature using the group key.
  2. Validates destination_url (must be https://, max 2048 chars, not on the blocked domain list).
  3. Applies your group's access rules (geographic filters, bot detection, etc.). Blocked visitors are redirected to a safe fallback.
  4. Serves the configured monetization flow (if any) for the group.
  5. Appends back_param (if provided) to destination_url and performs the final redirect.

If the signature is invalid or the destination URL fails validation, the visitor is silently redirected to the domain's default fallback URL — no error page is shown.

Limitations

Constraint Value
Destination URL scheme https:// required (http:// is auto-upgraded)
Destination URL max length 2048 characters (after back_param is appended)
Token version 0x01 — only this version is accepted
Key rotation If you delete and recreate a group, its key changes and old tokens become invalid
API rate limit None — tokens are generated locally; no request is made to Hozip's API
Documentation