Skip to main content

Node.js SDK

@propper/click-api-node is the server-side SDK for Propper Click. Use it when you need to record acceptances, list templates, or verify webhook signatures from a Node.js backend (Express, Fastify, NestJS, etc.).

Browser code?

For embedding the consent experience in a browser, use @propper/click-api-js instead.


Install

npm install @propper/click-api-node
# or
pnpm add @propper/click-api-node

Requires Node 18+ (uses global fetch).


Quick start

import { PropperClick } from '@propper/click-api-node';

const click = new PropperClick({
apiKey: process.env.PROPPER_API_KEY,
});

// Record an acceptance
const receipt = await click.acceptances.create({
templateVersionId: 'tv_...',
deploymentId: 'dep_...',
checksum: 'sha256-hex...',
consentMethod: 'BUTTON',
email: 'user@example.com',
});

console.log('Receipt:', receipt.receiptId);

The base URL defaults to https://api.propper.ai/v1/click. Override with baseUrl if needed.


Authentication

ModeHeaderUse when
API key (default)X-API-Key: <key>Server-to-server
OAuth bearerAuthorization: Bearer <jwt>Acting on behalf of a specific user
// API key (default when apiKey is set)
new PropperClick({ apiKey: 'sk_live_...' });

// OAuth bearer
new PropperClick({ authType: 'bearer', bearerToken: 'eyJ...' });

Get your API key from Settings → API Keys in the dashboard.


Resources

ResourceMethods
acceptancescreate(input, { idempotencyKey? })
templateslist, get, create, update, delete
deploymentslist, get, create
renderbyDeployment(id), byRoute({ domain, path })
verificationverifyReceipt({ receiptJson })

Idempotency

Acceptance writes auto-generate a UUID v4 idempotency key. To dedupe retries across processes, pass your own:

await click.acceptances.create(input, { idempotencyKey: requestId });

The SDK sends the key as X-Idempotency-Key; the server replays a cached response for repeat requests within 24 hours.


Webhook verification

When Propper sends you a webhook (e.g. click.acceptance.completed), verify the signature against the raw request body before parsing JSON:

import express from 'express';
import { verifyWebhookSignature } from '@propper/click-api-node';

const app = express();

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
verifyWebhookSignature({
body: req.body, // Buffer from express.raw
signatureHeader: req.header('X-Propper-Signature'),
secrets: process.env.PROPPER_WEBHOOK_SECRET,
});
} catch {
return res.status(400).send('invalid signature');
}
const event = JSON.parse(req.body.toString('utf8'));
// ... handle event
res.status(200).send('ok');
});

See webhook verification for details on signature format and rotation.


Error handling

import {
PropperClickAuthError,
PropperClickRateLimitError,
PropperClickValidationError,
} from '@propper/click-api-node';

try {
await click.acceptances.create(input);
} catch (err) {
if (err instanceof PropperClickAuthError) {
// rotate your API key
} else if (err instanceof PropperClickRateLimitError) {
// back off `err.retryAfterSeconds`
} else if (err instanceof PropperClickValidationError) {
// fix payload and retry
} else {
throw err;
}
}

Branch on type rather than message — types are stable, messages may change.


Configuration reference

new PropperClick({
apiKey: 'sk_live_...', // or bearerToken with authType: 'bearer'
environment: 'production', // production | staging | development | local
baseUrl: 'https://api.propper.ai/v1/click', // override env defaults
maxRetries: 3, // 5xx / 429 exponential backoff
timeoutMs: 30_000,
debug: false, // emit diagnostic logs to stderr
});