Quickstart: protect a document in five minutes

Create permanent, independently verifiable, court-ready proof that a file existed at a specific moment. Preferred for AI pairs: install the agent skill, complete its short setup, then let it store selected files on immut. Alternate: hash-only CLI/API when you must not upload bytes.

Agent summary

  • Goal: first protected document with court-ready proof.
  • Preferred: Agent Skill npx skills add enroh-ops/immut-agentimmut setup → store via POST /documents.
  • Live auth: API key (documents + folders scopes) + workspace + upload consent. README.
  • Alternate hash-only: CLI immut proof create --file --sidecar or POST /proofs with field hash only.
  • Verify (no key): GET https://backend.immut.io/api/public/verify/{txHash} or app.immut.io/verify.
  • Full agent playbook: agents.
  • Raw twin: /docs/quickstart.md

What do you get?

  • A permanent proof reference and document record on immut (store path), or proof ids from hash-only path.
  • A court-ready certificate PDF (when entitled).
  • Public verification anyone can run without an immut account.
  • With the public skill: files organised in folders for your business objective.

Preferred path: Agent Skill

npx skills add enroh-ops/immut-agent

Then, in your project folder, say to the agent (this is a chat phrase, not a shell command):

immut setup

Complete the five-question setup (objective → accept immut folder proposal → connect AI tools → entire project default → optional always-protect folder). It installs a daily recurring check by default and announces what it installed. Before it can protect anything you need:

  1. An API key from Organization Settings → AI Agents with documents:write, documents:read, folders:read, folders:write, certificates:read, workspaces:read.
  2. IMMUT_API_KEY and IMMUT_WORKSPACE_ID exported (list workspaces below).
  3. Then say immut sweep to protect what it finds, or immut protect for a single file.

Human + AI how-to: github.com/enroh-ops/immut-agent. Playbook: Agent playbook.

What do I need for live API calls?

  1. An API key at Organization Settings → AI Agents (API access must be enabled on the organisation). Keys look like imut_live_ + 32 characters; shown once.
  2. A workspace id:
export IMMUT_API_KEY="imut_live_..."

curl -s https://backend.immut.io/api/v1/workspaces \
  -H "Authorization: Bearer $IMMUT_API_KEY"
{ "success": true, "data": [ { "_id": "6a343b4d0de29d485cd1f710", "name": "Default Workspace" } ] }
export IMMUT_WORKSPACE_ID="6a343b4d0de29d485cd1f710"

Store one file with REST (public skill path)

curl -s -X POST https://backend.immut.io/api/v1/documents \
  -H "Authorization: Bearer $IMMUT_API_KEY" \
  -F "file=@report-final.pdf" \
  -F "workspace=$IMMUT_WORKSPACE_ID"
# optional: -F "folder=<folderObjectId>"

Create folders with POST /api/v1/folders (name, workspace, optional parentFolder). Scope: folders:write.

Alternate path: CLI hash-only (no file upload)

Fingerprints locally and calls POST /proofs. Not the public skill path.

npm install -g immut-cli

immut proof create --file report-final.pdf --sidecar
immut cert <proofId> -o certificate.pdf
immut verify <txHash> --file report-final.pdf

Every command accepts --json.

Alternate path: REST hash-only (POST /proofs)

How do I build the hash field?

The API field is named hash. It must be the 64-character lowercase hex fingerprint of the file (SHA-256 of the file bytes). Compute it locally. Do not upload the file.

# macOS / Linux
shasum -a 256 report-final.pdf | cut -d' ' -f1
// Node.js (streams, large files)
const crypto = require('crypto');
const fs = require('fs');
const hash = crypto.createHash('sha256');
fs.createReadStream('report-final.pdf')
  .on('data', (d) => hash.update(d))
  .on('end', () => console.log(hash.digest('hex')));
# Python
import hashlib
print(hashlib.sha256(open("report-final.pdf", "rb").read()).hexdigest())

How do I create the proof?

curl -s -X POST https://backend.immut.io/api/v1/proofs \
  -H "Authorization: Bearer $IMMUT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "hash": "d28771e1bf6a7aa31693a4c81203b9cb1d51f9d1c20c1d7ad6db4035f4eaff6b",
    "workspace": "'$IMMUT_WORKSPACE_ID'",
    "fileName": "report-final.pdf",
    "fileSize": 48213,
    "mimeType": "application/pdf",
    "metadata": { "description": "Q2 risk assessment, finalised version" }
  }'

Successful create returns HTTP 201. Same content already proven in that workspace returns HTTP 200 with "alreadyProven": true and the original proof data.

{
  "success": true,
  "data": {
    "proofId": "6a55f4b3f8878465844f8b43",
    "txHash": "02790DA05EF2999C77A2B462CE0F7A1E11A52FB295F3AE3FF542112B12B7A8E5",
    "verifyUrl": "https://livenet.xrpl.org/transactions/02790DA05EF2999C77A2B462CE0F7A1E11A52FB295F3AE3FF542112B12B7A8E5/detailed",
    "certPath": "/api/v1/certificates/6a55f4b3f8878465844f8b43",
    "ledger": "mainnet",
    "ledgerIndex": 105588998,
    "timestamp": "2026-07-14T08:34:59.412Z",
    "hashScheme": "hmac-sha256-nonce-v3",
    "proofCommitment": "9f2c41d8a0b3e5f6c7d8e9fa0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c",
    "proofNonce": "4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f"
  }
}

Field notes (codebase-accurate):

  • txHash: permanent reference used for public verification.
  • verifyUrl: explorer link returned by the API for the transaction (opaque URL; optional for humans).
  • proofNonce: required to verify under the default salted scheme. Store with the file owner. Recover via GET /api/v1/proofs/{proofId}?includeSalt=true or the certificate PDF.
  • proofId: status polling and certificate download.
  • ledger / ledgerIndex: opaque network metadata from the API.

What must I store?

Write the response next to the file, for example report-final.pdf.immut.json. Keep at least proofId, txHash, and proofNonce.

Proof creation is synchronous: a 201 means the proof is complete. Poll GET /api/v1/proofs/{proofId} only if a response was lost or status shows processing.

Download the court-ready certificate PDF (scope certificates:read):

curl -s https://backend.immut.io/api/v1/certificates/$PROOF_ID \
  -H "Authorization: Bearer $IMMUT_API_KEY" -o certificate.pdf

How does anyone verify later?

Prefer the CLI or the browser verifier. Both need the original file; neither uploads it for the default flow.

immut verify 02790DA05EF2999C77A2B462CE0F7A1E11A52FB295F3AE3FF542112B12B7A8E5 --file report-final.pdf

Public keyless API:

curl -s https://backend.immut.io/api/public/verify/02790DA05EF2999C77A2B462CE0F7A1E11A52FB295F3AE3FF542112B12B7A8E5

Browser: app.immut.io/verify.

Do not treat verifyUrl from the create response as the only verify path. For independent checks use the public verify API, CLI, or web page. Cryptographic details of salted vs plain schemes: Verify a document.

What happens if I prove the same file twice?

Idempotent per fingerprint and workspace. A second POST with the same hash returns 200 with "alreadyProven": true and the original data (including nonce). Nothing is written twice and nothing extra is charged.

What errors should my integration handle?

StatusCodeMeaning
400INVALID_HASHhash is not a 64-character hex fingerprint
400INVALID_WORKSPACEworkspace missing or not a valid id
401Missing or invalid API key
403API_ACCESS_DISABLEDPlan does not include API access
403Key lacks required scope
429Rate limit; respect Retry-After
502XRPL_ERRORProof network error; safe to retry (failed attempt is rolled back)

Rate limits: 60 requests per minute and 10,000 per day per key.

Where do I go next?