@drafterie/node
v0.1.0
Published
Official Drafterie Node.js SDK — programmable agreements: create contracts, evaluate agreement rules, mint embedded signing sessions, verify webhooks.
Maintainers
Readme
@drafterie/node
Official Drafterie Node.js SDK — programmable agreements for your product: create contracts, declare agreement rules, evaluate business events, mint embedded signing sessions, and verify webhooks.
Requires Node 18.17+ (built-in fetch). Zero runtime dependencies.
npm install @drafterie/nodeServer-side only
Your API key (dft_live_… / dft_test_…) is a privileged credential — never
ship it to a browser. The browser side uses the embed loader
(https://drafterie.com/embed.js) with a short-lived embedUrl your backend
mints through this SDK.
Quickstart — gate a business action on a signed agreement
import Drafterie from '@drafterie/node';
const drafterie = new Drafterie({ apiKey: process.env.DRAFTERIE_API_KEY });
// 1. Declare a policy once (or build it in the portal's Rules page):
await drafterie.rules.create({
name: 'High-value purchase agreement',
triggerEvent: 'transaction.created',
conditions: [
{ field: 'amount', operator: 'greater_than_or_equal', value: 500 },
{ field: 'currency', operator: 'equals', value: 'CAD' },
],
agreement: {
contractType: 'sales-agreement',
jurisdiction: 'ontario',
signerRole: 'buyer',
parties: [{ role: 'seller', legalName: 'Your Store Inc.', email: '[email protected]' }],
variables: {
totalPrice: { fromPayload: 'amount' }, // pulled from each event
currency: 'CAD', // fixed on every agreement
goodsDescription: { fromPayload: 'description' },
governingLaw: 'ontario',
},
},
onDeclined: 'block',
});
// 2. At runtime, post the business event. One call returns the decision,
// the created agreement, AND an embeddable signing URL:
const result = await drafterie.events.evaluate({
event: 'transaction.created',
payload: { amount: 750, currency: 'CAD', description: 'One oak desk' },
signer: { name: 'Casey Customer', email: '[email protected]' },
externalReference: 'order_123', // your id — makes retries converge
});
if (result.outcome === 'agreement_required') {
// Hand this to your frontend; mount it with embed.js (inline or modal).
const embedUrl = result.agreement.embed.embedUrl; // 30-min, single-use
} else {
// No agreement needed — continue the action immediately.
}externalReference makes re-emitted events converge: a retried
evaluate for the same reference returns the same contract (with a fresh
embed token) instead of creating a duplicate.
Confirm completion with webhooks (the authoritative signal)
Never complete the business action off a browser event — wait for the signed webhook:
import express from 'express';
import Drafterie from '@drafterie/node';
app.post('/webhooks/drafterie', express.raw({ type: '*/*' }), (req, res) => {
let event;
try {
// Passing req.headers verifies the v2 signed timestamp
// (X-Drafterie-Signature-V2 + X-Drafterie-Timestamp, ±5min replay window),
// falling back to the v1 X-Drafterie-Signature header when v2 is absent.
event = Drafterie.webhooks.constructEvent(
req.body, // the RAW bytes — never re-serialize
req.headers,
process.env.DRAFTERIE_WEBHOOK_SECRET,
);
} catch {
return res.status(400).send('bad signature');
}
// At-least-once delivery: dedupe on the stable delivery id.
const deliveryId = req.get('X-Drafterie-Delivery-Id');
if (alreadyProcessed(deliveryId)) return res.sendStatus(200);
if (event.event === 'contract_completed') {
fulfillOrder(event.externalReference); // your idempotent handler
}
res.sendStatus(200);
});Direct contract APIs
// Create + send (template/compiler path — omit `content`):
const contract = await drafterie.contracts.create({
contractType: 'nda',
jurisdiction: 'ontario',
parties: [
{ role: 'disclosingParty', legalName: 'Acme Inc.', email: '[email protected]' },
{ role: 'receivingParty', legalName: 'Jane Smith', email: '[email protected]' },
],
variables: { governingLaw: 'ontario' },
externalReference: 'deal_42',
});
// Embedded signing for one party (30-min, single-use):
const { embedUrl } = await drafterie.contracts.createEmbedToken(contract.id, {
partyEmail: '[email protected]',
});
const fetched = await drafterie.contracts.retrieve(contract.id);
const { contracts } = await drafterie.contracts.list({ status: 'pending_signature' });
const pdfBytes = await drafterie.contracts.pdf(contract.id); // ArrayBuffer
await drafterie.contracts.cancel(contract.id, { reason: 'order refunded' });drafterie.agreements is an alias of drafterie.contracts.
Sandbox
A dft_test_ key targets a fully isolated sandbox: no real email is sent
(per-party signing URLs come back in testInbox), webhooks are recorded but
not delivered, and nothing touches production data or quota.
const sandbox = new Drafterie({ apiKey: process.env.DRAFTERIE_TEST_KEY });
const c = await sandbox.contracts.create({
/* … */
});
await sandbox.contracts.simulate(c.id, { action: 'sign' }); // synthetic lifecycleErrors
Every failure is a typed error — branch on instanceof, not message strings:
import { ValidationError, RateLimitError, NotFoundError } from '@drafterie/node';
try {
await drafterie.contracts.retrieve(id);
} catch (err) {
if (err instanceof NotFoundError) {
/* 404 (or wrong environment) */
} else if (err instanceof RateLimitError) {
/* err.retryAfterSeconds */
} else if (err instanceof ValidationError) {
/* err.field, err.messages */
}
// All API errors carry err.status, err.code, err.requestId
}Retries & idempotency
Network failures, 429s and 5xxs are retried with exponential backoff
(maxRetries, default 2). Mutations are auto-assigned an Idempotency-Key
(reused across retries), so a retry replays the original result instead of
re-executing — pass your own via { idempotencyKey } on create/evaluate calls
to extend that guarantee across process restarts.
Reference
drafterie.contracts—create,list,retrieve,pdf,cancel,resend,createEmbedToken,simulate(sandbox)drafterie.rules—create,list,retrieve,update,del,test(dry run)drafterie.events—evaluate,list(webhook reconciliation feed)drafterie.evaluations—list(rules-engine decision ledger)drafterie.usage(),drafterie.status(),drafterie.isSandbox()Drafterie.webhooks—verifySignature,verifySignatureV2,constructEvent
Full API docs: https://docs.drafterie.com
