Skip to main content
Delegated Access is currently in Private Beta.
Server-only
This page covers your server webhook handler. The client triggers delegation; your server verifies, decrypts, and stores materials.
When a delegation is triggered, your endpoint receives a webhook named wallet.delegation.created. The delegated materials are in data.

Example payload (wallet.delegation.created)

{
  "messageId": "f44da9f0-a5b5-47f6-965f-f04af51c903e",
  "eventId": "2cf779a8-89da-486f-974e-2b77b738e4ac",
  "eventName": "wallet.delegation.created",
  "timestamp": "2025-10-01T15:13:26.348Z",
  "webhookId": "9a31fefc-64e4-4551-81da-1502eacc852d",
  "userId": "7eb7843b-2a4d-4f69-b95e-d219f0662fda",
  "environmentId": "53728749-1f19-4cab-becf-b88f952c3a3c",
  "environmentName": "sandbox",
  "data": {
    "chain": "EVM",
    "encryptedDelegatedShare": {
      "alg": "HYBRID-RSA-AES-256",
      "iv": "dzePdAUMQd6lWQngEXWPdQ",
      "ct": "pJIT5UU...XcWeYsXhygL2QbQcWZK6Rs5_CuiCDb_dHC_7P1tC...",
      "tag": "Yq8bpMU8huIx7UzUUUgI9Q",
      "ek": "uix2E6E...Keru7HWqeu7ktw"
    },
    "encryptedWalletApiKey": {
      "alg": "HYBRID-RSA-AES-256",
      "ct": "PzeliI...0kB9C0",
      "ek": "iWJgZQ...rxt",
      "iv": "RpC5nw1b4udgJqnC1p0evQ",
      "kid": "dynamic_rsa_lSuvWlCy",
      "tag": "-ZtmOG6gYTzS53wVMNK0Ig"
    },
    "publicKey": "0xd74ff800a3c6f66ecd217118aaa6fb1c916fa4e2",
    "userId": "7eb7843b-2a4d-4f69-b95e-d219f0662fda",
    "walletId": "25193936-3ecd-4c1b-84e6-9eabc82e53c2"
  }
}

Verify → Decrypt → Store

  1. Verify the webhook signature. See Validate webhook signatures.
  2. Decrypt data.encryptedDelegatedShare and data.encryptedWalletApiKey.
  3. Store userId, walletId, and decrypted materials securely (e.g., envelope encryption, KMS, at-rest encryption).
Encryption fields
alg: hybrid (RSA‑OAEP + AES‑256‑GCM); iv: AES IV; ct: ciphertext; tag: GCM tag; ek: encrypted content‑encryption key; kid: key identifier for rotation.

Example: Node (verify + decrypt skeleton)

// Pseudocode skeleton — fill in your key loading and signature verification
import crypto from 'node:crypto';

type EncryptedPayload = {
  alg: string; iv: string; ct: string; tag: string; ek: string; kid?: string;
};

function decryptAesGcm(encryptedKey: Buffer, ivB64: string, ctB64: string, tagB64: string) {
  const iv = Buffer.from(ivB64, 'base64url');
  const ciphertext = Buffer.from(ctB64, 'base64url');
  const tag = Buffer.from(tagB64, 'base64url');
  const decipher = crypto.createDecipheriv('aes-256-gcm', encryptedKey, iv);
  decipher.setAuthTag(tag);
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
  return plaintext;
}

function rsaOaepDecryptEk(privateKeyPem: string, ekB64: string) {
  return crypto.privateDecrypt(
    { key: privateKeyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
    Buffer.from(ekB64, 'base64url')
  );
}

export function decryptMaterials(
  privateKeyPem: string,
  share: EncryptedPayload,
  apiKeyEnc: EncryptedPayload
) {
  const shareKey = rsaOaepDecryptEk(privateKeyPem, share.ek);
  const walletApiKeyKey = rsaOaepDecryptEk(privateKeyPem, apiKeyEnc.ek);

  const delegatedShare = decryptAesGcm(shareKey, share.iv, share.ct, share.tag);
  const walletApiKey = decryptAesGcm(walletApiKeyKey, apiKeyEnc.iv, apiKeyEnc.ct, apiKeyEnc.tag);

  return {
    delegatedShare: delegatedShare.toString('base64url'),
    walletApiKey: walletApiKey.toString('utf8'),
  };
}
If a delivery fails, you can replay it from the dashboard. Use the eventId as an idempotency key.

What's next?

Learn how to use the delegated materials in Developer Actions.
I