Console
Taqlyn Docs/REST APIs/Ed25519 Signed Requests Specification
API v1.43 min read

Ed25519 Signed Requests Specification

Cryptographic specification for authenticating backend requests using Ed25519 signatures.

LIVE SPECIMEN
Universal & Deferred Link Simulator
URLhttps://taqlyn.com/shop/checkout?cartId=cart_9841&promo=WELCOME20&aff=partner_8
Route: /shop/checkoutMode: Deferred Install

Privileged endpoints in the Taqlyn REST API require request signing with an Ed25519 private key. This prevents unauthorized short-link generation, protects against replay attacks, and ensures request integrity across the wire.

Official Server SDKs compute these headers automatically. If you are implementing a custom HTTP client or proxy, follow this exact specification.

Try signed sandbox calls in the browser from the Interactive API Reference (server SDK routes only).

#Required Headers

Every signed request must include:

HeaderDescriptionExample
X-Taqlyn-Client-IdActive application client ID.app_test_8f92ab140cd3
X-Taqlyn-TimestampCurrent Unix time in seconds.1725537600
X-Taqlyn-SignatureStandard (or URL-safe) base64 of the raw 64-byte Ed25519 signature.6e98b2c41804...

#Canonical Message Construction

Before signing, build the canonical message as six newline-separated fields (no trailing newline). This matches CanonicalRequestMessage in the API:

CODE
taqlyn-v1
{METHOD}
{PATH}
{unixTimestamp}
{clientID}
{hex(sha256(body))}
  1. taqlyn-v1: Fixed protocol version prefix (first line).
  2. METHOD: Uppercase HTTP verb (e.g. POST, PATCH, DELETE).
  3. PATH: Request path only — no query string (e.g. /v1/short-links).
  4. unixTimestamp: Decimal Unix seconds — the same value sent in X-Taqlyn-Timestamp.
  5. clientID: The app_test_* / app_live_* client ID (same as X-Taqlyn-Client-Id).
  6. hex(sha256(body)): Hex-encoded SHA-256 of the raw request body bytes. Empty body uses SHA-256 of zero bytes (e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855).

#Example Canonical String

CODE
taqlyn-v1
POST
/v1/short-links
1725537600
app_test_8f92ab140cd3
a8910ce2b834910f...

#Signing the Canonical String

Sign the UTF-8 bytes of the canonical string with your Ed25519 private key (PKCS#8 PEM). Encode the 64-byte signature as standard base64 (URL-safe and raw-URL-safe are also accepted by the API).

#Reference Implementation: TypeScript / Node.js

TypeScript
import { createHash, sign } from "node:crypto";

export function computeSignedHeaders(opts: {
  clientId: string;
  privateKeyPem: string;
  method: string;
  path: string;
  body: string | Buffer;
}): Record<string, string> {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const bodyHash = createHash("sha256")
    .update(opts.body || "")
    .digest("hex");

  const canonical = [
    "taqlyn-v1",
    opts.method.toUpperCase(),
    opts.path,
    timestamp,
    opts.clientId,
    bodyHash,
  ].join("\n");

  const signature = sign(null, Buffer.from(canonical, "utf8"), opts.privateKeyPem).toString(
    "base64",
  );

  return {
    "X-Taqlyn-Client-Id": opts.clientId,
    "X-Taqlyn-Timestamp": timestamp,
    "X-Taqlyn-Signature": signature,
  };
}

#Reference Implementation: Go

Go
package main

import (
	"crypto/ed25519"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
	"time"
)

func SignRequest(clientID string, privKey ed25519.PrivateKey, method, path string, body []byte) (map[string]string, error) {
	ts := time.Now().Unix()
	sum := sha256.Sum256(body)
	canonical := fmt.Sprintf(
		"taqlyn-v1\n%s\n%s\n%d\n%s\n%s",
		strings.ToUpper(method),
		path,
		ts,
		clientID,
		hex.EncodeToString(sum[:]),
	)
	sig := ed25519.Sign(privKey, []byte(canonical))

	return map[string]string{
		"X-Taqlyn-Client-Id": clientID,
		"X-Taqlyn-Timestamp": strconv.FormatInt(ts, 10),
		"X-Taqlyn-Signature": base64.StdEncoding.EncodeToString(sig),
	}, nil
}

#Security Tolerances & Verification

  • Timestamp Drift Limit: The API rejects requests where abs(now - X-Taqlyn-Timestamp) > 300 seconds (401).
  • Signature Algorithm: Only Ed25519 is accepted. RSA or ECDSA keys are rejected.
  • Environment isolation: Sandbox (app_test_*) and production (app_live_*) keypairs never cross-validate.