X Enterprises
fastify-xplaid

webhooks.getVerificationKey(keyId)

Retrieve the JWK used to validate Plaid webhook payload signatures.

webhooks.getVerificationKey(keyId)

Retrieves the JWK (JSON Web Key) that Plaid uses to sign webhook payloads. The keyId comes from the Plaid-Verification header on each incoming webhook request. Cache the key and only re-fetch when you see a new keyId.

Signature

fastify.xplaid.webhooks.getVerificationKey(
  keyId: string
): Promise<{ key: WebhookVerificationKey; requestId: string }>

Params

NameTypeRequiredDescription
keyIdstringYesThe key ID from the Plaid-Verification header of the incoming webhook.

Returns

Promise<{ key: WebhookVerificationKey, requestId: string }>

FieldTypeDescription
key.algstringSigning algorithm, e.g. "ES256".
key.created_atnumberUnix timestamp when the key was created.
key.crvstringElliptic curve name, e.g. "P-256".
key.expired_atnumber | nullUnix timestamp when the key expired, or null if still active.
key.kidstringKey ID matching the Plaid-Verification header value.
key.ktystringKey type, e.g. "EC".
key.usestringIntended use, "sig".
key.xstringEC public key x-coordinate (base64url).
key.ystringEC public key y-coordinate (base64url).

Throws

  • [xPlaid] keyId is required and must be a non-empty string
  • Plaid API errors — shape: { message, statusCode, plaidError }.

Examples

Basic — fetch verification key

const { key } = await fastify.xplaid.webhooks.getVerificationKey(keyId);
console.log(key.alg); // "ES256"

Realistic — webhook receiver with signature verification

// Simple in-process key cache
const keyCache = new Map();

fastify.post("/webhooks/plaid", {
  config: { rawBody: true }, // enable rawBody for signature verification
}, async (request, reply) => {
  const keyId = request.headers["plaid-verification"];
  if (!keyId) return reply.status(400).send({ error: "Missing verification header" });

  // Fetch and cache the JWK
  let verificationKey = keyCache.get(keyId);
  if (!verificationKey) {
    try {
      const { key } = await fastify.xplaid.webhooks.getVerificationKey(keyId);
      verificationKey = key;
      keyCache.set(keyId, key);
    } catch (error) {
      request.log.error({ keyId }, "Failed to fetch webhook verification key");
      return reply.status(401).send({ error: "Could not verify webhook" });
    }
  }

  // Verify the JWT signature (use jose or similar)
  const isValid = await verifyWebhookSignature(request.rawBody, verificationKey);
  if (!isValid) {
    request.log.warn("Invalid Plaid webhook signature");
    return reply.status(401).send({ error: "Invalid signature" });
  }

  // Route by event type
  const { webhook_type, webhook_code, item_id } = request.body;
  request.log.info({ webhook_type, webhook_code, item_id }, "Plaid webhook received");

  if (webhook_type === "TRANSACTIONS" && webhook_code === "SYNC_UPDATES_AVAILABLE") {
    await enqueueTransactionSync(item_id);
  } else if (webhook_type === "ITEM" && webhook_code === "PENDING_EXPIRATION") {
    await notifyUserReauthRequired(item_id);
  }

  return reply.send({ received: true });
});

See also


AI Context

package: "@xenterprises/fastify-xplaid"
method: fastify.xplaid.webhooks.getVerificationKey(keyId)
use-when: Retrieve the JWKS verification key to validate incoming Plaid webhook signatures — cache by keyId
returns: { key: WebhookVerificationKey, requestId }
Copyright © 2026