Point an approved existing system at Lumra over a webhook or the events API. Lumra seals the event on receipt and, when you enroll a device or platform key, separately verifies the source's own signature. Each record states which of those checks occurred. No new hardware is required for a source that can send HTTPS, and raw media need not be uploaded.
For an approved pilot, Lumra can seal the original webhook JSON without a payload formatter or new hardware. PriviNet issues tenant credentials and checks the deployed endpoint and delivery behavior before you enable live traffic. Intake sealing and optional source-signing are reported separately.
# Example request shape for an approved TTN pilot. Set LUMRA_PILOT_URL and # LUMRA_PILOT_KEY only after onboarding and endpoint acceptance. curl -X POST "$LUMRA_PILOT_URL/v1/ingest/raw" \ -H "Authorization: Bearer $LUMRA_PILOT_KEY" \ -H "Content-Type: application/json" \ -d '{"end_device_ids":{"device_id":"reefer-8842"},"received_at":"2026-07-10T01:00:00Z","uplink_message":{"decoded_payload":{"temperature":11.5}}}' # Illustrative response after an accepted delivery { "event_id": "493c7708-...", "proof_status": "cloud_signed" }
/v1/ingest/raw and derives a tenant-scoped identity for ordinary TTN retries when no explicit event identity is supplied. Onboarding is controlled: PriviNet issues pilot credentials and confirms tenant isolation, retry behavior, and the deployed revision before live traffic is enabled. The TTN-specific retry fix is not represented here as already deployed to production. Read the controlled-pilot guide.With pilot credentials, dry-run a payload before sending it for real. You get back the record and hash Lumra would seal, and nothing is stored or billed:
# Validate any payload; nothing is recorded or metered. curl -X POST https://api.privinet.net/v1/events/dry-run \ -H "Authorization: Bearer pk_live_your_key" \ -H "Content-Type: application/json" \ -d '{"adapter":"raw","payload":{"end_device_ids":{"device_id":"reefer-8842"}, ...}}' { "ok": true, "would_seal": { "event_type": "raw.event", "source_id": "reefer-8842", "normalized_payload_sha256": "a5510938..." } }
Post an event and get the full result back in one call. Authenticate with your partner key in the Authorization header.
Point your platform's webhook at Lumra and sign each delivery with your webhook secret. A retry carrying the same event identity can return the original record instead of creating another.
Authorization: Bearer <key>; platforms using signed webhooks also receive a webhook secret. The public sample files in the section below are static, not a live pilot test. The interactive docs at /docs have an Authorize button for an issued key.curl https://privinet.net/sample-proofpack.jsonfe9c7bb5085e7b6e6bff71388cc71a6f54cc93e264002ea381c0eb8e3c6cdf2c; Python standalone; OpenSSL is required for RFC-3161 timestamp verification; any change to a sealed value fails).INDETERMINATE. This site does not claim an EU-qualified ProofPack or eIDAS compliance.# Memory-care example: a resident wandering (geofence exit). The "metalert" # adapter normalizes this to a gps.* event, which runs the wandering-risk # analyzer. Risk rises with: a geofence EXIT (not enter), night local time # (derived from timestamp + longitude), low battery, active movement, and no # caregiver acknowledgment. Generic event_types get a signed record but no # memory-care risk interpretation, so send these fields to get it. curl -X POST https://api.privinet.net/v1/events \ -H "Authorization: Bearer pk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "adapter": "metalert", "payload": { "external_event_id": "SMCG-ELP-2026-07-07-001", "device_id": "resident-wearable-3B", "event_type": "geofence_exit", "timestamp": "2026-07-07T09:14:00Z", "lat": 34.0522, "lng": -118.2437, "battery": 17, "speed": 1.6, "zone_id": "home", "zone_name": "Home", "caregiver_acknowledged": false } }' # Response (trimmed): a signed, chained record you can read back by id. { "event_id": "164b999f-...", "status": "reported", "analysis_summary": { "classification": "high_risk_wandering", "risk_score": 1.0, "urgency": "critical", "escalation_required": true, "recommended_action": "Notify the primary caregiver immediately..." }, "proof_status": "cloud_signed", "proof_id": "b2f1c3a4-...", "report_id": "9d8e7f6a-..." } # Idempotent by design, on ANY adapter: retry with the SAME external_event_id in # the payload, OR send an "Idempotency-Key: <your-id>" header, and you get the # SAME event back ("deduped": true), never a duplicate. Safe to retry on timeout.
Endpoint Does Auth POST /v1/events Ingest one event through the full pipeline, return the signed result Bearer key GET /v1/events/{id} Read an event back Bearer key GET /v1/events/{id}/analysis Risk score, escalation decision, explanation factors Bearer key GET /v1/events/{id}/proof The signed proof record Bearer key POST /v1/events/{id}/verify Recompute the hashes and re-check the signature Bearer key GET /v1/events/{id}/proofpack A portable ProofPack export for that event Bearer key POST /v1/webhooks/{partner} Signed inbound receiver (see below) HMAC signature
Compute an HMAC-SHA256 of the exact request body using your webhook secret, and send it as X-Signature: sha256=<hex>. A missing or wrong signature is rejected. A repeated external_event_id returns the original result, so retries never double-record.
# Sign and deliver, in a few lines. BODY='{"adapter":"metalert","payload":{"external_event_id":"evt-1", ...}}' SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk '{print $2}')" curl -X POST https://api.privinet.net/v1/webhooks/metalert \ -H "X-Signature: $SIG" \ -H "Content-Type: application/json" \ -d "$BODY" # First delivery -> creates the record. Retried delivery -> { "deduped": true, "event_id": "164b999f-..." }
By default Lumra seals every event at intake. To have the record also carry your device or platform's own signature, enroll that key's public half once, then sign each event at the source. Lumra verifies the signature against your enrolled key and the record comes back edge_signed_verified, over the normal webhook or events API, with no query parameter. Enrolling a key you do not control gains nothing: only real signatures from it verify. The private seed never leaves your machine; only the public key is enrolled and published.
# 1. Enroll your device or platform public key (once). curl -X POST https://api.privinet.net/v1/portal/source-keys \ -H "Authorization: Bearer pk_live_your_key" \ -H "Content-Type: application/json" \ -d '{"key_id":"reefer-8842","public_key_hex":"<your ed25519 public key hex>","signer_type":"edge_device_key","label":"reefer logger"}'
Sign the SHA-256 of the canonical payload at the source. The canonicalization must match Lumra byte-for-byte: sorted keys at every level, compact separators, UTF-8, and no ASCII-escaping of unicode.
# 2. Sign each event at the source (Python; a Node version is identical in shape). import hashlib, json from nacl import signing def canonical(payload): return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode() sk = signing.SigningKey(bytes.fromhex(SEED_HEX)) # your secret 32-byte seed digest = hashlib.sha256(canonical(payload)).hexdigest() sig = sk.sign(digest.encode()).signature.hex() incoming_proof = { "signature": sig, "signer_type": "edge_device_key", "key_id": "reefer-8842", "algorithm": "ed25519", "signed_payload_hash": digest, }
Send it on the events API or a webhook exactly as before, with the incoming_proof object alongside the payload. The response confirms your source signature was verified:
# 3. Source-signed over the normal path, no query parameter. { "incoming_proof_status": "edge_signed_verified", "proof_status": "platform_secondary_signature" }
To bind related events into one cross-vendor incident, set the same incident_label on each event. Place it inside payload, or at the top level of the request next to adapter; either works. Then fetch the fused, time-ordered readout at GET /v1/incidents/<label>.
No engineering on the device itself? Drop a small signer in front of your existing feed instead: it canonicalizes, signs, and forwards, so your events become source-signed with no change to the device. Records from several vendors, each source-signed with its own key, can be bound into one cross-vendor incident: see the multi-source Incident ProofPack demo.
Three rungs, fastest first, and all three verify identically. Point your feed at /v1/ingest/raw for sealed records with no code. Run the signing proxy for source-signed records with no change to your devices. Or sign in your own service with the SDK.
Byte-compatible Python and Node reference implementations. Generate a key, enroll it once, sign each event in-process. Includes a runnable TTN example. Apache-2.0, open source.
A small service that sits in front of your existing feed: it canonicalizes, signs with your key, and forwards, so events become source-signed with no change to the device. A helper, not required for cloud-signed raw ingest.
The adapter field selects how your raw payload is normalized into the common event shape. More adapters are added per partner need.
Supported records can be re-checked independently by recomputing protected hashes and checking signatures against an accepted trust root. The endpoint below performs server-side checks; a ProofPack carries the material and instructions needed for offline review. A certification template can support a qualified person's process under FRE 902(13)/(14), but the package does not decide admissibility.
# Re-check a record. Any change to a sealed value returns tamper_detected. curl -X POST https://api.privinet.net/v1/events/{id}/verify \ -H "Authorization: Bearer pk_live_your_key" { "verification_status": "cloud_signed", "detail": "hashes recomputed, signature valid" }
Sixty days at fixed scope as a paid design-partner pilot: one line of business, one data source. We issue pilot credentials, validate the deployed path with synthetic data, and connect approved live traffic only after the written acceptance checks pass.
Request a design-partner slot