Logo Lava

Docs Signing

Signed URLs

Paid features need a signature on the address. You make it on your server with a secret only you hold.

B Klug, updated October 27, 2026

Private designs, AI drawn marks and the kit API (/v2/kit.zip) are paid features. A request for one of them has to carry api_key and sig. The signature proves that the address was made by the owner of the key, so nobody can copy your key from a web page and spend your allowance. The free features never need a signature.

Your signing secret

Every paid account has a signing secret on its account page, in the box called Signing secret. It is a long random string. Keep it on your server, in an environment variable or a secrets store, and never in a web page, an app or a public repository. The site itself never puts it in your browser: the Logo Maker and the account page ask the server to sign for you.

You can change the secret from the same box. The page asks twice, because a new secret breaks every address you have signed so far: they stop drawing until you sign them again. Private designs themselves are not affected. Your private design space does not depend on the secret, so a design you made stays yours after a change.

How a signature is made

  1. Take every parameter of the address, api_key included, and leave out sig.
  2. Sort the parameters by name, and by value when a name repeats.
  3. Write them as name=value pairs joined with &, percent-encoded the standard way: every character except the letters A to Z and a to z, the digits and - . _ ~ becomes % and two capital hex digits, and a space is %20. The encoding a browser's form uses is accepted too (a space is + and * stays as it is), so you are not stuck on whichever your language's library gives.
  4. Compute HMAC-SHA256 of that string with your signing secret, write it as lowercase hex, and keep the first 24 characters.
  5. Add the result to the address as sig.

The order of parameters in the final address does not matter. The service sorts them again before it checks. A change to any parameter, or a missing one, makes the signature fail and the request gets a 401.

Code

Node

import { createHmac } from 'node:crypto';

export function sign(secret, params) {
  const pairs = Object.entries(params).sort(([a, x], [b, y]) => (a < b ? -1 : a > b ? 1 : x < y ? -1 : x > y ? 1 : 0));
  const canonical = new URLSearchParams(pairs).toString();   // the browser's form encoding, which is accepted
  const sig = createHmac('sha256', secret).update(canonical).digest('hex').slice(0, 24);
  return 'https://api.logolava.com/v2/logo.png?' + canonical + '&sig=' + sig;
}

sign(process.env.LOGOLAVA_SECRET, { text: 'Juniper & Vale', seed: '1140956701', private: '1', api_key: process.env.LOGOLAVA_KEY });

PHP

function ll_sign(string $secret, array $params): string {
    ksort($params, SORT_STRING);
    $canonical = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
    $sig = substr(hash_hmac('sha256', $canonical, $secret), 0, 24);
    return 'https://api.logolava.com/v2/logo.png?' . $canonical . '&sig=' . $sig;
}

This signs a list with each name once. A name that repeats needs the pairs sorted by value too, so build the list yourself in that case.

Python

import hashlib, hmac
from urllib.parse import quote, urlencode

def sign(secret, params):
    pairs = sorted(params.items())
    canonical = urlencode(pairs, quote_via=quote)
    sig = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()[:24]
    return 'https://api.logolava.com/v2/logo.png?' + canonical + '&sig=' + sig

curl and openssl

Q='api_key=YOUR_KEY&private=1&seed=1140956701&text=Juniper%20%26%20Vale'
SIG=$(printf '%s' "$Q" | openssl dgst -sha256 -hmac "$LOGOLAVA_SECRET" -hex | awk '{print substr($NF,1,24)}')
curl -o logo.png "https://api.logolava.com/v2/logo.png?$Q&sig=$SIG"

Here you write the canonical form yourself: parameters in name order, already encoded.

If a request fails

  • 401 with a bad signature. The most common cause is a parameter added after signing, or the secret from another account. Sign the final list of parameters, and sign again after any change.
  • 402 with the plan image. The signature is fine, but the plan does not include the feature. A private design needs Pro or Business, and the kit API needs Business.

The API keys page covers the key itself. Private designs are drawn from your account's own seed space: the same name and seed give you your design and give everyone else a different one.

Next: Private designs