Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

JavaScript / TypeScript Integration

Direct HTTP integration with fetch (Node.js 18+, Deno, Bun, or browser).


Start a Verification

const response = await fetch('https://pylonid.eu/v1/verify/age', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.PYLON_API_KEY}`
  },
  body: JSON.stringify({
    policy: { minAge: 18 },
    callbackUrl: 'https://yourapp.com/webhooks/pylon'
  })
});

const data = await response.json();
console.log('Verification:', data.verificationId);
console.log('Wallet URL:', data.walletUrl);
// Display data.walletUrl as QR code

Handle Webhooks (Express)

import express from 'express';
import crypto from 'crypto';

const app = express();

function validateSignature(body, signature, secret) {
  const computed = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computed));
}

app.post('/webhooks/pylon', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-pylon-signature'];

  if (!validateSignature(req.body, signature, process.env.PYLON_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const payload = JSON.parse(req.body);

  if (payload.status === 'verified') {
    console.log(`✅ ${payload.verificationId}: verified`);
  } else {
    console.log(`❌ ${payload.verificationId}: ${payload.status}`);
  }

  res.status(200).json({ received: true });
});

app.listen(3000);

Handle Webhooks (Next.js API Route)

// app/api/webhooks/pylon/route.ts
import crypto from 'crypto';
import { NextRequest, NextResponse } from 'next/server';

function validateSignature(body: Buffer, signature: string, secret: string): boolean {
  const computed = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computed));
}

export async function POST(req: NextRequest) {
  const body = Buffer.from(await req.arrayBuffer());
  const signature = req.headers.get('x-pylon-signature') ?? '';
  const secret = process.env.PYLON_WEBHOOK_SECRET!;

  if (!validateSignature(body, signature, secret)) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const payload = JSON.parse(body.toString());

  if (payload.status === 'verified') {
    // Grant access
  }

  return NextResponse.json({ received: true });
}

TypeScript Types

interface VerifyAgeRequest {
  policy: { minAge: number };
  callbackUrl: string;
}

interface VerifyAgeResponse {
  verificationId: string;
  status: string;
  walletUrl: string;
  requestUri: string;
  expiresAt: string;
}

interface WebhookPayload {
  event: 'verification.completed';
  verificationId: string;
  status: 'verified' | 'rejected' | 'expired' | 'error';
  result: { age_over_18?: boolean };
  timestamp: string;
}

Error Handling

const response = await fetch('https://pylonid.eu/v1/verify/age', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.PYLON_API_KEY}`
  },
  body: JSON.stringify({
    policy: { minAge: 18 },
    callbackUrl: 'https://yourapp.com/webhooks/pylon'
  })
});

if (!response.ok) {
  switch (response.status) {
    case 401: console.error('Invalid API key'); break;
    case 400: console.error('Invalid request'); break;
    case 429: console.error('Rate limited'); break;
    default:  console.error(`Error: ${response.status}`); break;
  }
}

Reference: API Reference | Webhooks | Troubleshooting