# 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-agent` → `immut setup` → store via `POST /documents`. > - Live auth: API key (documents + folders scopes) + workspace + upload consent. [README](https://github.com/enroh-ops/immut-agent#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](/docs/agents). > - Raw twin: [/docs/quickstart.md](/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 ```bash 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): ```text 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](https://app.immut.io/organization/settings) 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](https://github.com/enroh-ops/immut-agent#readme). Playbook: [Agent playbook](/docs/agents). ## What do I need for live API calls? 1. **An API key** at [Organization Settings → AI Agents](https://app.immut.io/organization/settings) (API access must be enabled on the organisation). Keys look like `imut_live_` + 32 characters; shown once. 2. **A workspace id:** ```bash export IMMUT_API_KEY="imut_live_..." curl -s https://backend.immut.io/api/v1/workspaces \ -H "Authorization: Bearer $IMMUT_API_KEY" ``` ```json { "success": true, "data": [ { "_id": "6a343b4d0de29d485cd1f710", "name": "Default Workspace" } ] } ``` ```bash export IMMUT_WORKSPACE_ID="6a343b4d0de29d485cd1f710" ``` ## Store one file with REST (public skill path) ```bash 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=" ``` 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. ```bash npm install -g immut-cli immut proof create --file report-final.pdf --sidecar immut cert -o certificate.pdf immut verify --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. ```bash # macOS / Linux shasum -a 256 report-final.pdf | cut -d' ' -f1 ``` ```javascript // 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 # Python import hashlib print(hashlib.sha256(open("report-final.pdf", "rb").read()).hexdigest()) ``` ### How do I create the proof? ```bash 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. ```json { "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`): ```bash 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. ```bash immut verify 02790DA05EF2999C77A2B462CE0F7A1E11A52FB295F3AE3FF542112B12B7A8E5 --file report-final.pdf ``` Public keyless API: ```bash curl -s https://backend.immut.io/api/public/verify/02790DA05EF2999C77A2B462CE0F7A1E11A52FB295F3AE3FF542112B12B7A8E5 ``` Browser: [app.immut.io/verify](https://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](/docs/verify). ## 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? | Status | Code | Meaning | |---|---|---| | 400 | `INVALID_HASH` | `hash` is not a 64-character hex fingerprint | | 400 | `INVALID_WORKSPACE` | `workspace` missing or not a valid id | | 401 | | Missing or invalid API key | | 403 | `API_ACCESS_DISABLED` | Plan does not include API access | | 403 | | Key lacks required scope | | 429 | | Rate limit; respect `Retry-After` | | 502 | `XRPL_ERROR` | Proof 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? - [API reference](/docs/api) (raw: [/docs/api.md](/docs/api.md)) - [Agent playbook](/docs/agents) (raw: [/docs/agents.md](/docs/agents.md)) - [Verify a document](/docs/verify) (raw: [/docs/verify.md](/docs/verify.md)) - Machine bootstrap: `GET https://backend.immut.io/api/v1/docs`