@certnode/sdk
v2.1.0
Published
CertNode SDK — cryptographic provenance for AI outputs. Sign content from Claude, OpenAI, Mistral, or any model with independently verifiable receipts. Complete rewrite from v1.x (unrelated to previous API surface).
Maintainers
Readme
@certnode/sdk
Cryptographic provenance for AI outputs. Sign content from Claude, OpenAI, Mistral, or any model with independently verifiable receipts.
Install
npm install @certnode/sdkQuickstart
Get an API key at https://certnode.io/ai-provenance (100 signings/month free).
Sign AI output:
import { CertNode } from '@certnode/sdk'
import Anthropic from '@anthropic-ai/sdk'
const claude = new Anthropic()
const cert = new CertNode({ apiKey: process.env.CERTNODE_API_KEY! })
const response = await claude.messages.create({
model: 'claude-opus-4-7',
messages: [{ role: 'user', content: 'Write marketing copy for X' }],
})
const signed = await cert.signAIOutput({
output: response.content[0].text,
model: 'claude-opus-4-7',
provider: 'anthropic',
})
console.log(signed.receiptId)
console.log(signed.verifyUrl)
// Anyone can verify at: certnode.io/verify/<receiptId>- Verify later (no API key required for verification):
const verification = await cert.verify({ receiptId: signed.receiptId })
console.log(verification.valid) // true
console.log(verification.receipt?.signedAt)OpenAI
import OpenAI from 'openai'
import { CertNode } from '@certnode/sdk'
const openai = new OpenAI()
const cert = new CertNode({ apiKey: process.env.CERTNODE_API_KEY! })
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Summarize this article' }],
})
const signed = await cert.signAIOutput({
output: completion.choices[0].message.content!,
model: 'gpt-4o',
provider: 'openai',
})What gets signed
Every call to signAIOutput produces a receipt with:
- Content hash — sha256 of the output (not the raw content; re-hash to verify)
- JWS signature — ES256 signature over the payload
- Three-layer timestamps:
- CertNode internal timestamp (HMAC-SHA256, sub-second)
- RFC 3161 timestamp from an independent TSA (court-admissible standard)
- Bitcoin anchor via OpenTimestamps (queued, confirmed within 1-2 hours)
- Metadata — model, provider, optional prompt hash
Verification
Anyone can verify a signature — no account required:
// Mode 1: by receipt ID (looks up stored receipt)
await cert.verify({ receiptId: 'uuid-here' })
// Mode 2: by raw signature + content (re-hashes and verifies)
await cert.verify({
signature: signed.signature,
content: 'original content here',
})Or use the hosted verify page: https://certnode.io/verify/[receiptId]
Why this matters
- AI detection is losing. Every model release breaks GPTZero, Turnitin, and other detectors. Provenance is cryptographic — it doesn't degrade.
- Designed for court admissibility under FRE 902(13)/(14). Three-layer timestamp chain provides self-authenticating digital evidence.
- EU AI Act Article 50 compliant (enforces August 2026). Content provenance for AI-generated content.
- Platform-agnostic. Works with Claude, OpenAI, Mistral, Meta, or any AI tool. Not locked to one provider.
Pricing
| Monthly volume | Per-signing | |---|---| | 0 – 100 | Free | | 100 – 10,000 | $0.010 | | 10,000 – 100,000 | $0.007 | | 100,000 – 1,000,000 | $0.004 | | 1,000,000+ | $0.002 |
Volume discounts apply automatically. Verifications are always free.
API reference
new CertNode(options)
interface CertNodeOptions {
apiKey: string // Required. cn_live_... or cn_test_...
baseUrl?: string // Default: https://certnode.io/api/v1/provenance
timeoutMs?: number // Default: 15000
}cert.signAIOutput(input)
interface SignAIOutputInput {
output: string // AI-generated text to sign
model?: string // e.g. 'claude-opus-4-7'
provider?: string // e.g. 'anthropic'
promptHash?: string // sha256 of prompt (optional, privacy)
}cert.signContent(input)
interface SignContentInput {
content: string
contentType?: 'ai_output' | 'image' | 'document' | 'json'
model?: string
provider?: string
promptHash?: string
}cert.verify(input)
interface VerifyInput {
// Mode 1: by receipt ID
receiptId?: string
// Mode 2: by raw signature + content
signature?: string
content?: string
}cert.getReceipt(receiptId) (v2.1.0)
Look up a receipt by ID. Convenience wrapper around verify({ receiptId })
that returns just the receipt object if valid. Throws CertNodeError with
code 'not_found' if the receipt doesn't exist or is invalid. Verification
is free; this does not consume API quota.
const receipt = await cert.getReceipt('uuid-here')
console.log(receipt.signedAt)
console.log(receipt.timestamps.bitcoin?.status) // 'anchored' | 'pending' | 'skipped'cert.searchRegistry(input) (v2.1.0)
Search the org's signed receipts. Scoped to the API key's organization. Returns the structured export bundle that the dashboard uses — self-describing for compliance / eDiscovery hand-off.
interface SearchRegistryInput {
q?: string // substring against id / model / provider
from?: string // ISO date (signed_at >=)
to?: string // ISO date (signed_at <=, inclusive of full day)
apiKeyId?: string // filter to a single API key
limit?: number // default 100, max 10000
}
const results = await cert.searchRegistry({
from: '2026-05-01',
to: '2026-05-31',
q: 'gpt-4o',
})
console.log(results.export.row_count)
for (const r of results.receipts) {
console.log(r.id, r.signed_at, r.verify_url)
}cert.getTrustScore(receiptId) (v2.1.0)
Compute a developer-facing trust score (0-100) from a receipt's verification signals. The score weights the cryptographic chain (signature 40 + RFC 3161 20
- Bitcoin anchored 30 + age bonuses). NOT a regulatory or legal metric — a developer-facing convenience number. For legal admissibility framing, always cite the underlying receipt itself (designed for FRE 902(13)/(14) self-authenticating digital evidence).
const trust = await cert.getTrustScore('uuid-here')
console.log(trust.score) // 95
console.log(trust.signals.bitcoinAnchored) // true
console.log(trust.signals.ageHours) // 26.4Errors
All errors throw CertNodeError:
import { CertNodeError } from '@certnode/sdk'
try {
await cert.signAIOutput({ output: '...' })
} catch (err) {
if (err instanceof CertNodeError) {
console.log(err.code) // e.g. 'free_tier_exceeded'
console.log(err.status) // HTTP status
}
}Common error codes:
missing_api_key— no apiKey passed to constructorinvalid_or_revoked_api_key— key revoked or malformedfree_tier_exceeded— over 100/mo without a subscriptioncontent_required— empty contentcontent_too_large— over 1 MBtimeout— request exceeded timeoutMs
Links
- Homepage: https://certnode.io/ai-provenance
- Docs: https://certnode.io/ai-provenance/docs
- MCP server: https://www.npmjs.com/package/@certnode/mcp-sign
- Source: https://github.com/srbryant86/certnode/tree/main/packages/sdk
- License: MIT
