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.).
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
| Mode | Header | Use when |
|---|---|---|
| API key (default) | X-API-Key: <key> | Server-to-server |
| OAuth bearer | Authorization: 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
| Resource | Methods |
|---|---|
acceptances | create(input, { idempotencyKey? }) |
templates | list, get, create, update, delete |
deployments | list, get, create |
render | byDeployment(id), byRoute({ domain, path }) |
verification | verifyReceipt({ 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
});