API Reference
API Documentation
v1OperationalEverything you need to integrate Prima Evidence proof of existence into your applications.
https://api.primaevidence.comGetting Started
Four steps from zero to your first blockchain proof via API.
Create Account
Sign up at the Developer Dashboard with your email and password.
Generate API Key
Create a pk_live_... key from the dashboard. Save it — shown only once.
Purchase Credits
Buy prepaid credits. 1 credit = 1 proof. Bulk discounts available.
Make API Calls
POST SHA-256 hashes to /api/v1/proofs. Permanent Arweave storage.
Quick Start
Create your first proof in under 30 seconds:
Set your credentials before running examples:
# Add to your .env or export in terminal PRIMA_API_KEY=pk_live_YOUR_API_KEY PRIMA_BASE_URL=https://api.primaevidence.com
importort { createHash } fromm "crypto"ypto"; importort { readFile } fromm "fs/promises"/promises"; constst API_KEY = process.env.PRIMA_API_KEY!; constst BASE_URL = "https://api.primaevidence.com"tps://api.primaevidence.com"; // 1. Hash your file with SHA-2561. Hash your file with SHA-256 constst file = awaitit readFile("./document.pdf"document.pdf"); constst hash = createHash("sha256"a256").update(file).digest("hex"x"); // 2. Create proof via API2. Create proof via API constst response = awaitit fetch(`${BASE_URL}/api/v1/proofs`BASE_URL}/api/v1/proofs`, { method: "POST"ST", headers: { "Authorization"thorization": `Bearer ${API_KEY}`arer ${API_KEY}`, "Content-Type"ntent-Type": "application/json"plication/json", }, body: JSON.stringify({ hash, metadata: { title: "document.pdf"cument.pdf" }, }), }); if(!response.ok) { constst body = awaitit response.json().catchch(() => ({})); throwow new Error(body.error || `HTTP ${response.status}`TP ${response.status}`); } constst { data } = awaitit response.json(); console.log(`Proof ID: ${data.proofId}`oof ID: ${data.proofId}`); console.log(`Status: ${data.status}`atus: ${data.status}`); console.log(`Credits remaining: ${data.creditsRemaining}`edits remaining: ${data.creditsRemaining}`);
Authentication
Dashboard Access
Sign in with email and password at /developer to manage API keys, purchase credits, and view usage.
API Authentication
All API requests require a valid API key passed in the Authorization header as a Bearer token.
Authorization: Bearer pk_live_<64-hex-characters>
Generate API keys from your Developer Dashboard. Keys use the format pk_live_... and are shown once at creation.
pk_live_[a-f0-9]{64}Architecture Overview
InfrastructureEvery proof flows through a deterministic pipeline — from your application to permanent blockchain storage. Built on Cloudflare's global edge network with Durable Objects for atomic state management.
Your Application
Compute SHA-256 hash of any file or data
Prima API
Validate hash, deduct credits atomically
Durable Object
State machine with retry logic
Arweave Blockchain
Immutable, permanent storage
Technology Stack
Typical response times: POST /proofs ~200ms (includes atomic credit deduction), GET /proofs/:id ~100ms, GET /proofs ~150ms (scales with count). API-created proofs skip payment stages and begin at processing immediately.
Proof Lifecycle
Every proof progresses through a deterministic status pipeline. Typical confirmation time is under 60 seconds.
Hash submitted, Arweave upload in progress
Permanently stored on Arweave blockchain
Upload failed — credits refunded automatically
Endpoints
Create a single proof of existence. Deducts 1 credit. The hash is stored permanently on Arweave blockchain.
Request Body
{ "hash"sh": "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"fd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", "metadata"tadata": { "title"tle": "Q4 Financial Report" Financial Report", "description"scription": "Quarterly earnings report SHA-256"arterly earnings report SHA-256" } }
Response 200
{ "success"ccess": truee, "data"ta": { "proofId"oofId": "a3f8c2e1-7b4d-4e6f-9a1c-3d5e7f2b8c4a"f8c2e1-7b4d-4e6f-9a1c-3d5e7f2b8c4a", "hash"sh": "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"fd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", "status"atus": "processing"ocessing", "creditsUsed"editsUsed": 1 "creditsRemaining"editsRemaining": 49 "createdAt"eatedAt": "2026-02-27T14:30:00.000Z"26-02-27T14:30:00.000Z" } }
Example
curl -XPOSThttps://api.primaevidence.com/api/v1/proofs \ -H"Authorization: Bearer pk_live_YOUR_API_KEY"orization: Bearer pk_live_YOUR_API_KEY" \ -H"Content-Type: application/json"ent-Type: application/json" \ -d'{ "hash": "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", "metadata": { "title": "Q4 Financial Report" } }' "hash": "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", "metadata": { "title": "Q4 Financial Report" } }'
Webhook Events
LiveReceive real-time notifications when proof status changes. Configure a webhook endpoint in your Developer Dashboard to get instant updates.
Webhook Configuration
Register your endpoint URL in the Developer Dashboard. All webhook payloads are signed with HMAC-SHA256 for verification.
POST https://your-app.com/webhooks/prima Content-Type: application/json X-Prima-Signature: sha256=...
{ "event"ent": "proof.confirmed"oof.confirmed", "data"ta": { "proofId"oofId": "a3f8c2e1-7b4d-4e6f-9a1c-3d5e7f2b8c4a"f8c2e1-7b4d-4e6f-9a1c-3d5e7f2b8c4a", "hash"sh": "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"fd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", "arweaveTransactionId"weaveTransactionId": "m787dI694Tv43-uPRDzdi6ZG..."87dI694Tv43-uPRDzdi6ZG...", "arweaveUrl"weaveUrl": "https://arweave.net/m787dI694Tv43..."tps://arweave.net/m787dI694Tv43...", "confirmedAt"nfirmedAt": "2026-02-27T14:30:45.000Z"26-02-27T14:30:45.000Z" }, "timestamp"mestamp": "2026-02-27T14:30:45.000Z"26-02-27T14:30:45.000Z" }
{ "event"ent": "proof.failed"oof.failed", "data"ta": { "proofId"oofId": "a3f8c2e1-7b4d-4e6f-9a1c-3d5e7f2b8c4a"f8c2e1-7b4d-4e6f-9a1c-3d5e7f2b8c4a", "hash"sh": "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"fd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", "reason"ason": "Arweave upload timeout"weave upload timeout", "creditsRefunded"editsRefunded": 1 }, "timestamp"mestamp": "2026-02-27T14:35:00.000Z"26-02-27T14:35:00.000Z" }
Signature Verification
importort crypto fromm "crypto"ypto"; functionction verifyWebhookSignature( payload: string, signature: string, secret: string ): boolean { constst expected = crypto .createHmac("sha256"a256", secret) .update(payload) .digest("hex"x"); returnurn crypto.timingSafeEqual( Buffer.fromm(signature), Buffer.fromm(`sha256=${expected}`a256=${expected}`) ); } // Express/Hono handlerExpress/Hono handler app.post("/webhooks/prima"ebhooks/prima", (req, res) => { constst signature = req.headers["x-prima-signature"prima-signature"]; constst rawBody = JSON.stringify(req.body); if(!verifyWebhookSignature(rawBody, signature, WEBHOOK_SECRET)) { returnurn res.status(401).json({ error: "Invalid signature"valid signature" }); } constst { event, data } = req.body; switch (event) { case "proof.confirmed"oof.confirmed": console.log(`Proof ${data.proofId} confirmed on Arweave`oof ${data.proofId} confirmed on Arweave`); console.log(`TX: ${data.arweaveTransactionId}`: ${data.arweaveTransactionId}`); breakak; case "proof.failed"oof.failed": console.log(`Proof ${data.proofId} failed: ${data.reason}`oof ${data.proofId} failed: ${data.reason}`); breakak; } res.json({ received: truee }); });
Webhooks are retried with exponential backoff for up to 24 hours. Your endpoint should return 2xx within 15 seconds. Failed deliveries are logged in your Developer Dashboard.
Response Headers
Every API response includes credit and rate limit information in headers.
X-Credits-RemainingYour current credit balance after this requestX-Credits-UsedCredits consumed by this request (0 for read operations)X-RateLimit-LimitMaximum requests allowed in the current windowX-RateLimit-RemainingRequests remaining in the current windowX-RateLimit-ResetUnix timestamp when the rate limit window resetsError Codes
All errors return {"success":false,"error":"..."} with the appropriate HTTP status code.
Invalid or missing API key
{"success":false,"error":"Valid API key required"}Insufficient credits — purchase more to continue
Single: "Insufficient credits" | Batch: "Insufficient credits: need 5, have 3"Resource not found — proof ID does not exist or account not initialized
{"success":false,"error":"Proof not found"}Hash already exists as a proof
Single: "Hash already exists as a proof" | Batch: "Hash already exists: {hash}"Validation error — check request body format
{"success":false,"error":"hash: Must be a valid SHA-256 hex string"}Rate limit exceeded — retry after the indicated period
{"success":false,"error":"Too many requests"} + Retry-After headerInternal server error — contact support if persistent
{"success":false,"error":"Internal server error"}Troubleshooting
Common issues and step-by-step fixes. Contact support if problems persist.
- Verify your key starts with pk_live_ followed by exactly 64 hex characters
- Check the Authorization header format: Bearer pk_live_... (note the space after Bearer)
- Ensure the key has not been revoked in the Developer Dashboard
- This is expected idempotent behavior — you will not be double-charged
- Use GET /api/v1/proofs to find the existing proof for this hash
- To create a new proof, the file content must differ (producing a different SHA-256 hash)
- Check your balance via GET /api/v1/account or the X-Credits-Remaining response header
- Purchase more credits at primaevidence.com/developer
- For batch requests, your balance must cover all hashes in the batch (atomic deduction)
- Read the Retry-After response header for the number of seconds to wait
- Implement exponential backoff in your client (see the SDK Pattern section)
- Contact support for higher rate limits on enterprise plans
Rate Limits
Rate limits are applied per API key. Exceeding limits returns 429 with Retry-After header.
POST /api/v1/proofs60 req/minPOST /api/v1/proofs/batch10 req/minGET /api/v1/proofs/:id300 req/minGET /api/v1/proofs60 req/minGET /api/v1/account600 req/minRate limits are tracked per API key. When exceeded, responses include a Retry-After header indicating seconds to wait. Note: POST and GET to /api/v1/proofs share the same 60/min counter. Contact support for higher limits.
Security Best Practices
Best PracticesFollow these recommendations to keep your integration secure and production-ready.
Use Environment Variables
Never hardcode API keys in source code. Use .env files locally and secure secret managers in production.
Rotate Keys Regularly
Generate new keys periodically. Revoke old keys immediately from the Developer Dashboard after rotation.
Server-Side Only
Never expose API keys in client-side code, mobile apps, or browser JavaScript. Always call the API from your backend.
HTTPS Enforced
All API endpoints enforce HTTPS. HTTP requests are rejected. TLS 1.2+ is required for all connections.
Idempotent Operations
Duplicate hash submissions return 409 Conflict. Batch operations are atomic — partial failures never occur.
Handle Errors Gracefully
Implement exponential backoff for 429 and 5xx errors. Check X-Credits-Remaining headers to prevent 402 errors.
Environment Template
# .env.local — Prima Evidence API Configurationv.local — Prima Evidence API Configuration # Generate your API key at https://primaevidence.com/developererate your API key at https://primaevidence.com/developer PRIMA_API_KEY=pk_live_YOUR_API_KEY_HERE PRIMA_API_BASE=https://api.primaevidence.com PRIMA_WEBHOOK_SECRET=whsec_YOUR_WEBHOOK_SECRET # Optional: Override timeout (default 30s)ional: Override timeout (default 30s) PRIMA_TIMEOUT_MS=30000
SDK Pattern
RecommendedCopy-paste client class with typed methods, error handling, and exponential backoff polling. Production-ready for any language.
// npm install @prima-evidence/sdknpm install @prima-evidence/sdk importort { PrimaClient, PrimaApiError } fromm "@prima-evidence/sdk"rima-evidence/sdk"; importort { createHash } fromm "crypto"ypto"; importort { readFile } fromm "fs/promises"/promises"; constst prima = new PrimaClient(process.env.PRIMA_API_KEY!); // Hash a fileHash a file constst buffer = awaitit readFile("./contract.pdf"contract.pdf"); constst hash = createHash("sha256"a256").update(buffer).digest("hex"x"); // Create proof and wait for blockchain confirmationCreate proof and wait for blockchain confirmation constst { proofId, creditsRemaining } = awaitit prima.createProof(hash, { title: "Contract v2.1"ntract v2.1", }); console.log(`Proof created: ${proofId} (${creditsRemaining} credits left)`oof created: ${proofId} (${creditsRemaining} credits left)`); constst confirmed = awaitit prima.waitForConfirmation(proofId); console.log(`Arweave TX: ${confirmed.arweaveTransactionId}`weave TX: ${confirmed.arweaveTransactionId}`); // Batch create (up to 100 proofs atomically)Batch create (up to 100 proofs atomically) constst batch = awaitit prima.createBatch([ { hash: "a".repeat(64, metadata: { filename: "file1.pdf"le1.pdf" } }, { hash: "b".repeat(64, metadata: { filename: "file2.pdf"le2.pdf" } }, ]); console.log(`Batch: ${batch.count} proofs, ${batch.creditsUsed} credits used`tch: ${batch.count} proofs, ${batch.creditsUsed} credits used`); // List proofs with paginationList proofs with pagination constst proofs = awaitit prima.listProofs({ page: 1limit: 10 status: "confirmed"nfirmed" }); console.log(`${proofs.pagination.total} total proofs`proofs.pagination.total} total proofs`); // Error handlingError handling try { awaitit prima.createProof(hash); } catchch (error) { if(error instanceof PrimaApiError) { console.error(`${error.status}: ${error.message}`error.status}: ${error.message}`); if(error.status === 402) console.error("Buy more credits"y more credits"); if(error.status === 409) console.error("Hash already registered"sh already registered"); } }
Changelog
API version history and upcoming features. We follow semantic versioning with backward-compatible changes.
Initial Public Release
- REST API with SHA-256 proof creation and batch operations (up to 100 per request)
- Permanent Arweave blockchain storage via Irys bundler network
- Prepaid credit system with bulk pricing tiers (up to 20% discount)
- API key authentication with prefix-based identification and instant revocation
- Real-time proof status tracking with webhook notifications