Aller au contenu
Trace & AnchorShield for protection, interlocking link for traceability, and checkmark for verification.Trace & Anchor

Trace & Anchor · Pro API v1

Proof of existence, from your own software

Anchor the fingerprint of any file on the Solana blockchain — optionally with an eIDAS qualified timestamp and a FranceConnect identity attestation — and receive a court-ready certificate and a machine-readable proof. Your files never leave your systems: you send a 64-character digest, nothing else.

FR — Ancrez l'empreinte de vos fichiers depuis vos logiciels ou un simple dossier sur le bureau. Les fichiers ne quittent jamais vos systèmes.

Quickstart

Base URL https://api-traceandanchor.com/api/v1. Create a key in Dashboard → Pro API, top up your wallet, then:

HASH=$(sha256sum invoice.pdf | cut -d' ' -f1)      # macOS: shasum -a 256

curl https://api-traceandanchor.com/api/v1/anchors \
  -H "Authorization: Bearer $TA_KEY" \
  -H "Idempotency-Key: sha256:$HASH" \
  -H "Content-Type: application/json" \
  -d "{\"file_hash\": \"$HASH\", \"original_filename\": \"invoice.pdf\"}"

curl -OJ -H "Authorization: Bearer $TA_KEY" https://api-traceandanchor.com/api/v1/anchors/<id>/certificate.pdf
curl -OJ -H "Authorization: Bearer $TA_KEY" https://api-traceandanchor.com/api/v1/anchors/<id>/proof.json

Python:

import hashlib, requests

API = "https://api-traceandanchor.com/api/v1"
H = {"Authorization": "Bearer ta_live_…"}

digest = hashlib.sha256(open("invoice.pdf", "rb").read()).hexdigest()
r = requests.post(f"{API}/anchors", headers={**H, "Idempotency-Key": f"sha256:{digest}"},
                  json={"file_hash": digest, "original_filename": "invoice.pdf",
                        "external_reference": "INV-2026-114"})
r.raise_for_status()
anchor = r.json()
print(anchor["certificate_number"], anchor["solana"]["explorer_url"])

Node.js 18+:

import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";

const digest = createHash("sha256").update(await readFile("invoice.pdf")).digest("hex");
const res = await fetch("https://api-traceandanchor.com/api/v1/anchors", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.TA_KEY}`, "Content-Type": "application/json",
             "Idempotency-Key": `sha256:${digest}` },
  body: JSON.stringify({ file_hash: digest, original_filename: "invoice.pdf" }),
});
const anchor = await res.json();

Authentication and keys

Send Authorization: Bearer ta_live_… (or X-API-Key). A key acts on behalf of the account that created it and spends its wallet. Scopes: anchors:write, anchors:read, identity. Keys are stored hashed and shown once; revoke them any time. Use them on servers and desktop software only — never in a web page or a mobile app. GET /account returns your balance, pricing and the key's scopes.

Computing the digest

Standard — hash_mode: "sha256", digest = SHA-256(file). Anyone holding the file can verify. Use it for photos, videos, scans — anything nobody could guess.

Protected — hash_mode: "hmac-sha256", digest = HMAC-SHA-256(K, file) with K = PBKDF2-HMAC-SHA256(NFKC(passphrase), salt, 600 000, 32 bytes). Send kdf_salt (16 random bytes, hex), kdf_iterations and kdf_algorithm — never the passphrase (requests carrying one are rejected). Use it whenever the file names a person: a template document where only a name and an amount change can otherwise be confirmed by guessing. Identical to the website, so protected anchors verify on /verify-hash with the passphrase.

K = hashlib.pbkdf2_hmac("sha256", unicodedata.normalize("NFKC", passphrase).encode(),
                        bytes.fromhex(salt), 600_000, 32)
digest = hmac.new(K, file_bytes, hashlib.sha256).hexdigest()

Idempotency

Anchoring costs money and networks fail. Send an Idempotency-Key on every POST /anchors: a retry with the same key and body within 24 h returns the original response (header Idempotent-Replayed: true) and is not charged again. Failed requests are not remembered, so after a 402 you can top up and retry with the same key. If Solana or Datasure fails, the wallet is refunded automatically.

Certificates and proofs

GET /anchors/{id}/certificate.pdf — the legal certificate (French). GET /anchors/{id}/proof.json — its machine-readable twin, built by the server from the record: digest and how it was computed, Solana transaction and cluster, issuer and LEI, level, timestamp, identity — enough to verify without us. GET /anchors/{id}/timestamp.tsr — the RFC 3161 token when timestamped. Files are named <declared file name>_<certificate id>.

Identity-level anchors (FranceConnect)

FranceConnect works through a browser redirect, so no server can authenticate a person on their behalf. The API uses approval sessions, like signing in to a TV:

  1. POST /identity/sessions → approval_url (valid 15 minutes).
  2. The account holder opens it, signs in with FranceConnect and clicks Approve.
  3. Poll GET /identity/sessions/{id} until status is approved.
  4. Pass identity_session_id to POST /anchors — valid one hour after the FranceConnect login.

What it proves: the identified person authenticated with FranceConnect and performed the anchoring. It does not prove ownership or authorship of the file — the certificate says so explicitly.

Desktop folders — no code

The desktop agent watches two folders on the desktop: 1 - Standard (SHA-256) for files without personal data and 2 - Protected (HMAC) for files that identify people. Drop a file in; its certificate and proof appear in Certificates. The passphrase is chosen once at setup and never leaves the computer.

python ta_agent.py setup   # API key, folder, protected passphrase (once)
python ta_agent.py run     # leave running

Download ta_agent.py — Python 3.9+, no other dependency.

Errors

Every error is JSON: {"detail": "…", "code": "insufficient_funds"}

HTTPcodeWhat to do
400invalid_request, invalid_digestFix the request
401not_authenticated, authentication_failedCheck or replace the key
402insufficient_fundsTop up the wallet, retry
403permission_denied, identity_session_not_approvedAdd the scope / approve a session
404not_foundWrong id, or another account's record
409idempotency_in_progressWait, then retry
422idempotency_key_reusedNew request → new Idempotency-Key
429throttledWait Retry-After seconds
502upstream_errorSolana/Datasure failed, wallet refunded — retry
503service_unavailableMaintenance — retry later

Rate limits and pricing

120 requests/minute per key; verification 30/min and 400/h. Charged per anchor from the prepaid wallet: €1.00 (Solana), +€3.00 eIDAS qualified timestamp, +€5.00 FranceConnect identity.

Trace & Anchor Pro API — Developer documentation | Trace & Anchor