Skip to main content
The signing SDK functions cover the full lifecycle of an Ave signature request: creating it server-side, presenting the UI to the user, polling for resolution, and verifying the result.
For a full walkthrough including when to use signing, payload design, and server-side enforcement patterns, see the Ave Signing guide.
bun add @ave-id/sdk

createSignatureRequest(config, params)

Creates a signature request for a specific identity. Server-side only — requires clientSecret.
function createSignatureRequest(
  config: {
    clientId: string;
    clientSecret: string;
    issuer?: string;
  },
  params: {
    identityId: string;
    payload: string;
    metadata?: Record<string, unknown>;
    expiresInSeconds?: number;
  }
): Promise<SignatureRequest>
config.clientSecret
string
required
Your app’s client secret. Never call this from browser code.
params.identityId
string
required
The Ave identity UUID (the sub claim from the user’s id_token). The identity must have a signing key set up, or this call fails with no_signing_key.
params.payload
string
required
The text the user will see and sign. Keep it human-readable — this is what the user approves. Max 10,000 characters.
params.metadata
object
Arbitrary JSON attached to the request record. Not shown to the user. Useful for audit logs and linking the request to your internal records.
params.expiresInSeconds
number
default:"300"
How long the request stays open before automatically transitioning to expired. Min 60, max 3600.
Returns SignatureRequest:
interface SignatureRequest {
  requestId: string;   // UUID — pass to the signing UI and status endpoint
  expiresAt: string;   // ISO 8601 expiry timestamp
  publicKey: string;   // Ed25519 public key in base64 — store for offline verification
}
// Server-side
import { createSignatureRequest } from "@ave-id/sdk";

const request = await createSignatureRequest(
  {
    clientId: process.env.AVE_CLIENT_ID!,
    clientSecret: process.env.AVE_CLIENT_SECRET!,
  },
  {
    identityId: "user-identity-uuid",
    payload: "I approve the transfer of $500 to account ending 4242",
    metadata: { action: "transfer", amount: 500 },
    expiresInSeconds: 300,
  }
);
// request.requestId — pass to the browser

getSignatureStatus(config, requestId)

Polls the current status of a signing request.
function getSignatureStatus(
  config: { clientId: string; issuer?: string },
  requestId: string
): Promise<SignatureResult>
Returns SignatureResult:
interface SignatureResult {
  status: "pending" | "signed" | "denied" | "expired";
  signature?: string;    // Ed25519 signature in base64 — present when status is "signed"
  resolvedAt?: string;   // ISO 8601 timestamp — present when signed or denied
}
When the request has passed its expiresAt timestamp and is still pending, the server automatically transitions it to expired when this endpoint is queried.
// Simple poll loop
async function waitForSignature(requestId: string): Promise<string | null> {
  for (let i = 0; i < 30; i++) {
    const result = await getSignatureStatus({ clientId: "YOUR_CLIENT_ID" }, requestId);
    if (result.status === "signed") return result.signature!;
    if (result.status !== "pending") return null;
    await new Promise(r => setTimeout(r, 2000));
  }
  return null; // timed out
}

verifySignature(config, params)

Verifies a message/signature/publicKey tuple via Ave’s API. Use this as a server-side check before executing any side effect.
function verifySignature(
  config: { issuer?: string },
  params: {
    message: string;
    signature: string;
    publicKey: string;
  }
): Promise<{ valid: boolean; error?: string }>
params.message
string
required
The signed payload string — exactly as it appeared in the signing request.
params.signature
string
required
The Ed25519 signature in base64, from the getSignatureStatus response.
params.publicKey
string
required
The Ed25519 public key in base64, from createSignatureRequest or the public key endpoint.
const result = await verifySignature(
  {},
  {
    message: "I approve the transfer of $500 to account ending 4242",
    signature: signatureFromStatus,
    publicKey: publicKeyFromRequest,
  }
);

if (!result.valid) {
  throw new Error(`Signature invalid: ${result.error}`);
}

// Safe to proceed with the side effect
You can also verify signatures locally using any Ed25519 library without calling this endpoint. Use the publicKey from createSignatureRequest, the signature from getSignatureStatus, and the original payload string.

buildSigningUrl(config, requestId, options?)

Builds the URL for the Ave signing page. Use this when you want to redirect the user to a full signing page rather than using a popup or embed.
function buildSigningUrl(
  config: { issuer?: string },
  requestId: string,
  options?: { embed?: boolean }
): string

openSigningPopup(config, requestId)

Opens a popup window for the signing flow. Returns a promise that resolves when the user signs, denies, or closes the popup. Rejects with an Error if the browser blocked the popup.
function openSigningPopup(
  config: { issuer?: string },
  requestId: string
): Promise<{ signed: boolean; signature?: string; publicKey?: string }>
try {
  const result = await openSigningPopup({}, requestId);

  if (result.signed) {
    // Verify server-side before taking action
    await verifyOnServer(result.signature, result.publicKey);
  } else {
    // User denied or closed the popup
  }
} catch (err) {
  // Popup was blocked by the browser — fall back to redirect or embed
  window.location.href = buildSigningUrl({}, requestId);
}
Last modified on May 1, 2026