Verification

Verify signed approval decisions using the public API endpoint or perform offline verification with the Ed25519 public key.

Key Concepts

Verification is the process of confirming that a signed approval decision is authentic — that it was really signed by the claimed approver and has not been tampered with. SignedApproval's verification is public and requires no authentication, so any third party can independently confirm an approval without trusting SignedApproval's servers.

There are two ways to verify: online verification via the API endpoint, and offline verification using the Ed25519 public key directly. Both produce the same mathematical result.

Online Verification

The simplest way to verify a decision is to call the public verification endpoint:

bash
curl https://signedapproval.net/api/v1/approvals/req_abc123/verify

No API key or authentication is required. The response includes the verification result:

JSON
{
  "valid": true,
  "decision": "approved",
  "method": "passkey",
  "verified_at": "2026-03-23T14:03:00.000Z",
  "public_key": "MCowBQYDK2VwAyEA...",
  "signature": "base64-ed25519-signature",
  "payload": {
    "v": 1,
    "rid": "req_abc123",
    "did": "dec_xyz789",
    "approver": "a1b2c3d4e5f6g7h8",
    "action": "9i8j7k6l5m4n3o2p",
    "decision": "approved",
    "method": "passkey",
    "ts": 1709000000,
    "exp": 1709086400,
    "nonce": "abcdef1234567890abcdef1234567890"
  }
}

The server performs the Ed25519 verification and returns valid: true or valid: false. It also logs the verification attempt in signedapproval_validations for audit purposes.

Offline Verification

Once you have the signature, payload, and public key (all returned by the online endpoint), you can verify offline without ever contacting SignedApproval again:

javascript
// Node.js offline verification
import { createPublicKey, verify } from 'crypto';

// These values come from a previous API call or are cached
const publicKeyDer = Buffer.from(publicKeyBase64, 'base64');
const signature = Buffer.from(signatureBase64, 'base64');

// Reconstruct the canonical payload with sorted keys
const payload = JSON.stringify(payloadObject, Object.keys(payloadObject).sort());
const payloadBytes = Buffer.from(payload);

const publicKey = createPublicKey({
  key: publicKeyDer,
  format: 'der',
  type: 'spki',
});

const isValid = verify(null, payloadBytes, publicKey, signature);
console.log('Offline verification:', isValid ? 'VALID' : 'INVALID');
python
# Python offline verification
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.hazmat.primitives.serialization import load_der_public_key
import base64, json

public_key_der = base64.b64decode(public_key_base64)
signature = base64.b64decode(signature_base64)

# Canonical payload with sorted keys
payload = json.dumps(payload_dict, sort_keys=True).encode()

public_key = load_der_public_key(public_key_der)
try:
    public_key.verify(signature, payload)
    print("Offline verification: VALID")
except Exception:
    print("Offline verification: INVALID")

What Verification Proves

  • Integrity — The payload has not been modified since signing. Changing any field invalidates the signature.
  • Authenticity — The decision was signed by the holder of the corresponding private key.
  • Non-repudiation — The approver cannot deny having made the decision (assuming their private key was not compromised).
  • Temporal binding — The timestamp and expiry are part of the signed payload, so they cannot be altered.
Note
The approver and action fields are hashed (first 16 chars of SHA-256) for privacy. If you need to confirm who approved or what was approved, you need the original user ID or action text to recompute the hash and compare.

Verification Audit Log

Every online verification attempt is logged in signedapproval_validations, recording:

  • The request ID that was verified
  • Whether verification succeeded or failed
  • The IP address of the verifier
  • Timestamp of the verification

This creates an audit trail of who checked which approvals and when, useful for compliance reporting.

Tip
For automated systems, we recommend caching the public key after the first verification call. Public keys do not rotate frequently, and caching lets you verify offline for subsequent decisions from the same approver.