# Create permanent proof from a local file fingerprint `POST /proofs` **Required scope:** `documents:write` The file never leaves the caller's environment. Send field hash (64-char hex fingerprint of file bytes). Idempotent per hash and workspace: re-posting returns 200 with alreadyProven true. Paid organisations get permanent proofs. Default salted scheme returns proofNonce once: store it for verification. Scope documents:write. ## Request body Content type: `application/json` (required) | Field | Type | Required | Description | |---|---|---|---| | `hash` | string | **yes** | 64-char hex fingerprint of the file (SHA-256 of file bytes) | | `workspace` | string | **yes** | Workspace id (GET /workspaces) | | `fileName` | string | no | | | `fileSize` | number | no | Bytes | | `mimeType` | string | no | | | `metadata` | object | no | | ## Responses | Status | Meaning | |---|---| | `200` | Already proven. These exact bytes were proven before, so the original proof is returned unchanged. | | `201` | Created. Keep proofId, txHash and proofNonce: without the nonce a salted proof cannot be verified later. | | `400` | BadRequest | | `401` | Unauthorized | | `403` | Forbidden | | `429` | RateLimited | | `502` | Proof network error (code XRPL_ERROR). Safe to retry: the attempt is rolled back. | ## Response fields (`200`) Returned inside `data`. | Field | Description | |---|---| | `proofId` | id for status polling and certificates | | `txHash` | permanent transaction reference used for public verification | | `verifyUrl` | public explorer link for the transaction (returned by API) | | `certPath` | /api/v1/certificates/{proofId} | | `ledger` | opaque network label (mainnet \| testnet) | | `ledgerIndex` | opaque index when available | | `timestamp` | ISO 8601 | | `hashScheme` | sha256-plain-v1 \| hmac-sha256-nonce-v3 | | `proofCommitment` | commitment value in the public proof when the scheme is salted | | `proofNonce` | KEEP THIS for hash-only salted proofs: required to verify later. Also embedded in the certificate PDF. | ## Example ```bash curl -X POST "https://backend.immut.io/api/v1/proofs" \ -H "Authorization: Bearer $IMMUT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"hash":"","workspace":"","fileName":"","fileSize":"","mimeType":"","metadata":""}' ``` ## OpenAPI fragment ```json { "/proofs": { "post": { "operationId": "createProof", "summary": "Create permanent proof from a local file fingerprint", "description": "The file never leaves the caller's environment. Send field hash (64-char hex fingerprint of file bytes). Idempotent per hash and workspace: re-posting returns 200 with alreadyProven true. Paid organisations get permanent proofs. Default salted scheme returns proofNonce once: store it for verification. Scope documents:write.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "hash", "workspace" ], "properties": { "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$", "description": "64-char hex fingerprint of the file (SHA-256 of file bytes)" }, "workspace": { "type": "string", "description": "Workspace id (GET /workspaces)" }, "fileName": { "type": "string", "maxLength": 255 }, "fileSize": { "type": "number", "description": "Bytes" }, "mimeType": { "type": "string" }, "metadata": { "type": "object", "properties": { "description": { "type": "string", "maxLength": 500 } } } } }, "example": { "hash": "43277ddf4e3c0f2fd11ef6a71652093256f889b1235d563da89eca03f5e28df6", "workspace": "6a343b4d0de29d485cd1f710", "fileName": "report-final.pdf", "fileSize": 48213, "mimeType": "application/pdf", "metadata": { "description": "Q2 risk assessment, finalised version" } } } } }, "responses": { "200": { "description": "Already proven. These exact bytes were proven before, so the original proof is returned unchanged.", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object", "properties": { "proofId": { "type": "string", "description": "id for status polling and certificates" }, "txHash": { "type": "string", "description": "permanent transaction reference used for public verification" }, "verifyUrl": { "type": "string", "description": "public explorer link for the transaction (returned by API)" }, "certPath": { "type": "string", "description": "/api/v1/certificates/{proofId}" }, "ledger": { "type": "string", "description": "opaque network label (mainnet | testnet)" }, "ledgerIndex": { "type": "string", "description": "opaque index when available" }, "timestamp": { "type": "string", "description": "ISO 8601" }, "hashScheme": { "type": "string", "description": "sha256-plain-v1 | hmac-sha256-nonce-v3" }, "proofCommitment": { "type": "string", "description": "commitment value in the public proof when the scheme is salted" }, "proofNonce": { "type": "string", "description": "KEEP THIS for hash-only salted proofs: required to verify later. Also embedded in the certificate PDF." } } } } } } } }, "201": { "description": "Created. Keep proofId, txHash and proofNonce: without the nonce a salted proof cannot be verified later." }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }, "502": { "description": "Proof network error (code XRPL_ERROR). Safe to retry: the attempt is rolled back." } }, "tags": [ "Proofs" ], "x-required-scope": "documents:write" } } } ```