Référence de l’API

API v2 de Sellat

Tout ce que fait l’application web, depuis votre propre système : enregistrer l’empreinte SHA-256 d’un fichier, l’ancrer sur Polygon et Bitcoin, demander l’horodatage qualifié de la FNMT, télécharger le certificat et le dossier de preuves, et conserver l’original. REST et JSON, avec une clé d’API.

URL de base https://sellat.app/api/v2

Introduction

L’API travaille avec l’empreinte du fichier, pas avec le fichier : vous calculez le SHA-256 chez vous et n’envoyez que ces 64 caractères. L’original ne quitte pas votre machine, sauf si vous choisissez de le confier en conservation.

Chaque preuve entre dans un lot Merkle ancré sur Polygon, et le même lot est attesté sur Bitcoin. Si vous le demandez, l’empreinte reçoit aussi l’horodatage qualifié de la FNMT-RCM, prestataire qualifié de la liste de confiance de l’UE.

Toutes les routes partent de l’URL de base et parlent JSON. Les preuves créées par l’API et sur le web appartiennent au même compte et apparaissent dans le même tableau de bord.

Elle est faite pour être appelée depuis un serveur : elle n’envoie pas d’en-têtes CORS, donc un navigateur ne peut pas l’appeler depuis un autre site, et votre clé ne doit jamais se trouver dans du code qui arrive au navigateur.

Authentification

Chaque requête porte votre clé dans l’en-tête Authorization: Bearer sellat_.... Les clés se créent et se révoquent depuis votre tableau de bord ; un compte peut avoir jusqu’à 5 clés actives, une par intégration.

Sans clé, ou avec une clé révoquée, l’API répond 401 unauthorized avec l’en-tête WWW-Authenticate: Bearer. La seule route publique est la preuve portable (/proof/{id}.json).

Requête
curl https://sellat.app/api/v2/account \
  -H "Authorization: Bearer sellat_3f9c..."

Démarrage rapide

Calculez l’empreinte, créez la preuve avec l’horodatage qualifié et gardez l’id renvoyé. En quelques minutes elle passe à anchored et toutes ses URL fonctionnent.

Sans "qualified": true, la preuve est tout aussi vérifiable (Polygon et Bitcoin) et ne consomme pas d’horodatage. Vous pouvez le demander plus tard avec POST /proofs/{id}/qualified.

Terminal
# Your key, from the dashboard
export SELLAT_API_TOKEN=sellat_...

# The fingerprint: the file stays on your machine
HASH=$(sha256sum contract.pdf | cut -d' ' -f1)

# The proof, with the FNMT qualified seal

curl https://sellat.app/api/v2/proofs \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"hash\":\"$HASH\",\"name\":\"contract.pdf\",\"qualified\":true}"

États d’une preuve

Le champ state dit ce que Sellat sait à chaque instant : il ne présente jamais comme confirmée une transaction qu’il n’a pas encore vérifiée.

L’attestation Bitcoin (bitcoin) suit son propre chemin : pending jusqu’à ce qu’OpenTimestamps l’inclue dans un bloc, puis confirmed avec sa hauteur. L’horodatage qualifié (qualified) ne dépend pas de l’état : il est émis tout de suite.

ÉtatSignification
queuedReçue. Attend d’entrer dans un lot, deux minutes environ au plus.
batchedDans un lot Merkle, chemin fixé. Attend l’ancrage sur Polygon.
anchoredAncrée : la transaction est dans un bloc Polygon. proof.json est disponible.
confirmedLe bloc a 12 confirmations.

Erreurs

Une erreur est toujours un JSON avec error.code (stable, pour votre code) et error.message (pour les personnes). Certaines ajoutent des champs, comme seals_remaining et buy_url sur un 402. La preuve portable, publique, répond {"error": "..."}.

Si la passerelle coupe une requête avant l’API (trop de requêtes depuis une IP, ou un corps de plus de 25 Mo), ce 429 ou ce 413 n’a pas ce format.

CodeHTTPCe qui s’est passé
invalid_request400Un champ manque ou n’a pas le format attendu. Le message dit lequel.
unauthorized401Pas de clé, clé mal formée ou révoquée.
payment_required402Plus d’horodatages qualifiés. Contient seals_remaining et buy_url.
key_without_account403La clé n’est rattachée à aucun compte (anciennes clés).
not_found404La preuve n’existe pas ou appartient à un autre compte.
conflict409Un autre compte a enregistré ces octets en premier, ou l’original n’est pas encore stocké.
hash_mismatch409Le fichier envoyé n’a pas l’empreinte de la preuve.
file_too_large413Le fichier dépasse la limite par fichier de votre offre (max_file_bytes).
rate_limited429Trop de requêtes par minute. Respectez Retry-After.
quota_exceeded429Limite quotidienne de preuves atteinte. Retry-After jusqu’à 00:00 UTC.
download_quota_exceeded429Limite quotidienne de téléchargements de l’original atteinte.
integrity_failed500L’original stocké ne correspond plus à son empreinte ; il n’est pas servi.
unavailable502 · 503Un service interne ne répond pas. Réessayez plus tard.
seal_unavailable503L’autorité d’horodatage n’est pas disponible pour le moment.
custody_unavailable503La conservation n’est pas disponible pour le moment.
custody_quota_exceeded507Plus d’espace de conservation sur votre compte.
Réponse
{
  "error": {
    "code": "payment_required",
    "message": "No qualified seals left on this account.",
    "seals_remaining": 0,
    "buy_url": "https://sellat.app/precios"
  }
}

Limites

Chaque compte peut créer un nombre de preuves par API et par jour, toutes clés confondues. Les preuves faites sur le web ne comptent pas, ni le renvoi d’octets qui vous appartiennent déjà. La journée repart à 00:00 UTC.

À la limite, POST /proofs répond 429 quota_exceeded avec un objet quota et Retry-After. Chaque route a aussi un frein par minute (par exemple 60 créations et 10 horodatages par minute) qui répond 429 rate_limited, et la passerelle accepte jusqu’à 120 requêtes par minute et par IP. Un corps de requête fait 25 Mo au plus.

Les horodatages qualifiés n’ont pas de limite quotidienne : chacun consomme un horodatage de votre solde. Consultez ce qu’il vous reste à tout moment avec GET /account, et pour plus de volume, écrivez à [email protected].

Compte gratuitCompte Pro
Preuves par API et par jour105 000
Clés actives55
Original en conservation, par fichier10 MB50 MB
Conservation au total50 MB5 GB
Téléchargements de l’original par jour1050
Réponse
HTTP/1.1 429 Too Many Requests
Retry-After: 41231

{
  "error": {
    "code": "quota_exceeded",
    "message": "Daily limit reached: 10 proofs per account per UTC day across all its keys.",
    "quota": {
      "limit": 10,
      "used": 10,
      "resets_in_seconds": 41231
    }
  }
}

Idempotence

Envoyez un en-tête Idempotency-Key (jusqu’à 200 caractères) avec POST /proofs et vous pourrez réessayer sans risque : si vous l’avez déjà utilisé avec la même clé d’API, vous recevez la preuve d’origine avec 200 et "created": false, quel que soit le nouveau corps, et aucun horodatage n’est émis.

Sans l’en-tête, renvoyer des octets qui appartiennent déjà à votre compte renvoie aussi la preuve existante (200), sans changer son nom ni ses métadonnées. Mais si un autre compte a enregistré ces octets en premier, chaque requête crée une nouvelle preuve avec limitations : si vous pouvez réessayer, utilisez toujours Idempotency-Key.

L’horodatage n’est jamais dupliqué non plus : une preuve en a un seul, et le redemander renvoie le même sans en consommer un autre.

Vérifier sans Sellat

Une preuve Sellat n’a pas besoin de Sellat pour être vérifiée. Avec le fichier et son proof.json, le vérificateur open source recalcule l’empreinte et le chemin Merkle, et lit l’ancrage sur la blockchain publique.

L’horodatage qualifié se vérifie avec OpenSSL, avec le fichier, le .tsr et le certificat de l’autorité, fourni dans le dossier de preuves. -partial_chain est nécessaire parce que la liste de confiance publie le certificat de l’autorité elle-même, pas celui d’une CA.

Terminal
# 1. The proof: file + proof.json + the public blockchain
curl https://sellat.app/api/v2/proof/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e.json -o proof.json
npx sellat-verify contract.pdf proof.json

# 2. The qualified seal: file + .tsr + the authority's certificate
#    (autoridad-sellado.pem comes inside evidence.zip)
openssl ts -verify -in seal.tsr -data contract.pdf \
  -CAfile autoridad-sellado.pem -partial_chain

Preuves

Créer une preuve

POST /proofs

Enregistre l’empreinte SHA-256 d’un fichier et renvoie la preuve. Avec "qualified": true, elle émet aussi l’horodatage qualifié de la FNMT dans le même appel, en consommant un horodatage du compte.

Paramètres

hash string · corps obligatoire
L’empreinte SHA-256 du fichier : 64 caractères hexadécimaux.
name string · corps facultatif
Le nom affiché dans le tableau de bord et sur le certificat. Seul le nom du fichier est gardé, sans le chemin. Par défaut, les 12 premiers caractères du hash.
metadata object · corps facultatif
Un objet JSON à vous, jusqu’à 4 Ko. Stocké avec la preuve, jamais renvoyé ni publié.
size integer · corps facultatif
Taille du fichier en octets. Informatif.
mime_type string · corps facultatif
Type du fichier, utilisé pour servir l’original si vous le conservez. Par défaut, application/octet-stream.
qualified boolean · corps facultatif
true émet aussi l’horodatage qualifié de la FNMT.
Idempotency-Key string · en-tête facultatif
Réessayer sans dupliquer (voir Idempotence).

Réponses

201
Preuve créée.
200
Elle existait déjà : ces octets appartiennent déjà à votre compte, ou vous avez répété une Idempotency-Key. Contient "created": false.
400
invalid_request : un champ manque ou n’est pas valide.
401
unauthorized.
402
payment_required : horodatage demandé, mais il ne vous en reste plus. Rien n’est écrit.
409
conflict : horodatage demandé sur des octets qu’un autre compte a enregistrés en premier.
429
quota_exceeded (limite quotidienne) ou rate_limited (par minute).
503
seal_unavailable ou unavailable. Si la preuve a été créée, l’erreur contient son proof_id.

Si l’autorité tarde à répondre, la preuve est quand même créée et qualified arrive comme {"state": "pending"} : redemandez-le avec POST /proofs/{id}/qualified, sans frais.

Si un autre compte a enregistré ces octets en premier, la preuve est créée et ancrée, mais sans certificat propre : les URL du certificat sont null et la réponse contient "limitations": ["hash_registered_by_another_account"].

Requête
curl https://sellat.app/api/v2/proofs \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: contract-2026-0142" \
  -d '{
    "hash": "9f3c2a5e0b7d1c4f8a6e2d9b3c7f1a5e8d2c6b0f4a9e3d7c1b5f8a2e6d0c4b9f",
    "name": "contract.pdf",
    "qualified": true,
    "metadata": { "ref": "case-2026-0142" }
  }'
Réponse
{
  "id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "created": true,
  "hash": "9f3c2a5e0b7d1c4f8a6e2d9b3c7f1a5e8d2c6b0f4a9e3d7c1b5f8a2e6d0c4b9f",
  "algorithm": "SHA-256",
  "name": "contract.pdf",
  "state": "queued",
  "received_at": "2026-09-30T09:12:03.418Z",
  "anchors": [],
  "bitcoin": {
    "state": "pending",
    "block_height": null
  },
  "qualified": {
    "state": "issued",
    "authority": "FNMT-RCM",
    "time": "2026-09-30T09:12:05.000Z",
    "serial_number": "175755C77B4239226AA0428FE1C107C1",
    "policy_oid": "0.4.0.2023.1.1",
    "tsr_url": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified.tsr"
  },
  "original": null,
  "urls": {
    "self": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
    "proof_json": "https://sellat.app/api/v2/proof/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e.json",
    "certificate": "https://sellat.app/certificate/3b1d7e42-9c5a-4f0e-b8d6-1a2c3e4f5a6b",
    "certificate_pdf": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/certificate.pdf",
    "evidence_zip": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/evidence.zip"
  }
}

Consulter une preuve

GET /proofs/{id}

Renvoie la preuve avec son état, ses ancrages, l’attestation Bitcoin, l’horodatage s’il existe, l’original s’il est conservé, et toutes ses URL.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
La preuve.
401
unauthorized.
404
not_found : elle n’existe pas ou appartient à un autre compte.
Requête
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e \
  -H "Authorization: Bearer $SELLAT_API_TOKEN"
Réponse
{
  "id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "hash": "9f3c2a5e0b7d1c4f8a6e2d9b3c7f1a5e8d2c6b0f4a9e3d7c1b5f8a2e6d0c4b9f",
  "algorithm": "SHA-256",
  "name": "contract.pdf",
  "state": "anchored",
  "received_at": "2026-09-30T09:12:03.418Z",
  "anchors": [
    {
      "network": "Polygon Mainnet",
      "chain_id": 137,
      "state": "anchored",
      "tx_hash": "0xa4195d4ea808610dee92a1caa221d35f289e61674f451c82f7428e29813f9d6a",
      "block_number": 94653624,
      "block_timestamp": "2026-09-30T09:14:36.000Z",
      "explorer_url": "https://polygonscan.com/tx/0xa4195d4ea808610dee92a1caa221d35f289e61674f451c82f7428e29813f9d6a"
    }
  ],
  "bitcoin": {
    "state": "pending",
    "block_height": null
  },
  "qualified": {
    "state": "issued",
    "authority": "FNMT-RCM",
    "time": "2026-09-30T09:12:05.000Z",
    "serial_number": "175755C77B4239226AA0428FE1C107C1",
    "policy_oid": "0.4.0.2023.1.1",
    "tsr_url": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified.tsr"
  },
  "original": null,
  "urls": {
    "self": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
    "proof_json": "https://sellat.app/api/v2/proof/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e.json",
    "certificate": "https://sellat.app/certificate/3b1d7e42-9c5a-4f0e-b8d6-1a2c3e4f5a6b",
    "certificate_pdf": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/certificate.pdf",
    "evidence_zip": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/evidence.zip"
  }
}

Lister les preuves

GET /proofs

Les preuves de votre compte, les plus récentes d’abord : celles créées par l’API et celles créées sur le web. Chaque élément de data est une preuve complète, comme GET /proofs/{id}.

Pour la page suivante, passez le next_cursor renvoyé par la précédente. Quand il vaut null, il n’y en a plus.

Paramètres

limit integer · query facultatif
Preuves par page, de 1 à 100. Par défaut, 25.
cursor string · query facultatif
Le next_cursor de la page précédente.

Réponses

200
data et next_cursor.
400
invalid_request : limit ou cursor invalide.
401
unauthorized.
Requête
curl "https://sellat.app/api/v2/proofs?limit=25" \
  -H "Authorization: Bearer $SELLAT_API_TOKEN"
Réponse
{
  "data": [
    {
      "id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
      "hash": "9f3c2a5e0b7d1c4f8a6e2d9b3c7f1a5e8d2c6b0f4a9e3d7c1b5f8a2e6d0c4b9f",
      "name": "contract.pdf",
      "state": "confirmed",
      "received_at": "2026-09-30T09:12:03.418Z"
    }
  ],
  "next_cursor": "1843"
}

Horodatage qualifié (eIDAS)

Demander l’horodatage qualifié

POST /proofs/{id}/qualified

Émet un horodatage RFC 3161 de la FNMT-RCM sur l’empreinte du fichier. Selon l’article 41, paragraphe 2, du règlement eIDAS, un horodatage électronique qualifié bénéficie d’une présomption d’exactitude de la date et de l’heure qu’il indique et d’intégrité des données.

Il consomme un horodatage du compte : celui de bienvenue ou l’un de vos packs. L’API ne débite jamais de carte et n’ouvre aucun paiement ; les packs s’achètent sur la page des tarifs.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
Horodatage émis, ou il existait déjà ("created": false, rien consommé). Contient seals_remaining.
202
L’horodatage est payé, mais l’autorité n’a pas répondu. Rappelez (Retry-After: 30) : il se termine sans frais.
401
unauthorized.
402
payment_required : plus d’horodatages. Contient seals_remaining et buy_url.
404
not_found.
409
conflict : un autre compte a enregistré ces octets en premier.
503
seal_unavailable.

Une preuve a un seul horodatage, pour toujours. Si l’émission échoue définitivement, l’horodatage revient à votre solde.

Requête
curl -X POST https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified \
  -H "Authorization: Bearer $SELLAT_API_TOKEN"
Réponse
{
  "proof_id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "created": true,
  "qualified": {
    "state": "issued",
    "authority": "FNMT-RCM",
    "time": "2026-09-30T09:12:05.000Z",
    "serial_number": "175755C77B4239226AA0428FE1C107C1",
    "policy_oid": "0.4.0.2023.1.1",
    "tsr_url": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified.tsr"
  },
  "seals_remaining": 4
}

Consulter l’horodatage

GET /proofs/{id}/qualified

Renvoie seulement l’horodatage de la preuve, ou null s’il n’y en a pas encore. N’émet rien et ne consomme rien.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
proof_id et qualified.
401
unauthorized.
404
not_found.
Requête
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified \
  -H "Authorization: Bearer $SELLAT_API_TOKEN"
Réponse
{
  "proof_id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "qualified": {
    "state": "issued",
    "authority": "FNMT-RCM",
    "time": "2026-09-30T09:12:05.000Z",
    "serial_number": "175755C77B4239226AA0428FE1C107C1",
    "policy_oid": "0.4.0.2023.1.1",
    "tsr_url": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified.tsr"
  }
}

Télécharger le jeton (.tsr)

GET /proofs/{id}/qualified.tsr

L’horodatage tel que l’autorité l’a renvoyé : une TimeStampResp RFC 3161 en DER. C’est le fichier qu’un expert ou un tribunal peut valider sans Sellat.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
Le .tsr (application/timestamp-reply).
401
unauthorized.
404
not_found : la preuve n’existe pas ou n’a pas encore d’horodatage.
Requête
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified.tsr \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -o seal.tsr
Réponse (fichier)
HTTP/1.1 200 OK
Content-Type: application/timestamp-reply
Content-Disposition: attachment; filename="sellat-6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e-qualified-timestamp.tsr"

<RFC 3161 TimeStampResp, DER>

Certificat et preuves

Certificat PDF

GET /proofs/{id}/certificate.pdf

Le certificat de la preuve, pour les personnes : l’empreinte, la date, les ancrages et l’horodatage qualifié s’il existe, avec les instructions pour le vérifier.

Paramètres

id string · chemin obligatoire
L’id de la preuve.
lang string · query facultatif
Langue du certificat : es, en, de ou fr. Toute autre valeur donne en.

Réponses

200
Le PDF.
401
unauthorized.
404
not_found.
409
conflict : un autre compte a enregistré ces octets en premier et la preuve n’a pas de certificat propre.
Requête
curl "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/certificate.pdf?lang=es" \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -o certificate.pdf
Réponse (fichier)
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="certificado-3b1d7e42-9c5a-4f0e-b8d6-1a2c3e4f5a6b.pdf"

<PDF>

Dossier de preuves

GET /proofs/{id}/evidence.zip

Un ZIP avec ce qu’il faut pour défendre la preuve sans Sellat : le certificat, le proof.json, l’horodatage et le certificat de l’autorité s’il existe, les instructions et, s’il est conservé, l’original.

Paramètres

id string · chemin obligatoire
L’id de la preuve.
lang string · query facultatif
Langue des textes du dossier : es, en, de ou fr.

Réponses

200
Le ZIP.
401
unauthorized.
404
not_found.
409
conflict : un autre compte a enregistré ces octets en premier.

Joindre l’original consomme l’un des téléchargements du jour. S’il n’en reste plus, le dossier sort sans lui et le README le dit.

Requête
curl "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/evidence.zip?lang=es" \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -o evidence.zip
Réponse (fichier)
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="sellat-evidencia-3b1d7e42-9c5a-4f0e-b8d6-1a2c3e4f5a6b.zip"

<ZIP>

Conservation de l’original

Déposer l’original

PUT /proofs/{id}/original

Stocke le fichier à côté de sa preuve, sur des serveurs de l’Union européenne. Sellat recalcule l’empreinte de ce qu’il reçoit et le refuse s’il ne correspond pas à la preuve.

Envoyez les octets bruts avec le Content-Type du fichier, ou en multipart/form-data dans le champ file. POST fonctionne comme PUT.

Paramètres

id string · chemin obligatoire
L’id de la preuve.
file binary · corps obligatoire
Le fichier. Jusqu’à 10 MB avec le compte gratuit et 50 MB avec le compte Pro, et 25 Mo par requête au plus.

Réponses

201
Stocké. Renvoie la preuve avec original.
200
Il était déjà stocké.
400
invalid_request : corps vide ou illisible.
401
unauthorized.
404
not_found.
409
hash_mismatch (ce n’est pas le fichier de la preuve) ou conflict (octets d’un autre compte).
413
file_too_large : au-delà de la limite par fichier de votre offre. Contient max_file_bytes.
502
unavailable : le stockage n’a pas répondu.
503
custody_unavailable.
507
custody_quota_exceeded : plus d’espace sur votre compte. Contient used_bytes et max_account_bytes.

La conservation est facultative : la preuve vaut autant sans l’original. Conserver ajoute la préservation ; cela ne conditionne pas la preuve.

Requête
curl -X PUT https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/original \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -H "Content-Type: application/pdf" \
  --data-binary @contract.pdf
Réponse
{
  "id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "state": "anchored",
  "original": {
    "stored": true,
    "file_name": "contract.pdf",
    "size_bytes": 184320,
    "mime_type": "application/pdf",
    "stored_at": "2026-09-30T09:15:10.000Z",
    "url": "https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/original"
  }
}

Télécharger l’original

GET /proofs/{id}/original

Renvoie le fichier conservé. Sellat recalcule son empreinte à la sortie et refuse de le servir s’il ne correspond plus. Chaque téléchargement compte dans la limite quotidienne de votre offre ; l’en-tête X-Sellat-Custody-Downloads-Remaining indique combien il en reste.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
Le fichier, avec son type et son nom.
401
unauthorized.
404
Aucun original en conservation.
409
conflict : l’original n’est pas encore stocké.
429
download_quota_exceeded : limite quotidienne de téléchargements. Repart à 00:00 UTC.
500
integrity_failed : ce qui est stocké ne correspond plus à l’empreinte.
502
unavailable.
Requête
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/original \
  -H "Authorization: Bearer $SELLAT_API_TOKEN" \
  -o contract.pdf
Réponse (fichier)
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="contract.pdf"
X-Sellat-Custody-Downloads-Remaining: 9

<the original bytes>

Mettre fin à la conservation

DELETE /proofs/{id}/original

Supprime l’original conservé. La preuve ne change pas : elle reste ancrée, horodatée et vérifiable.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
"original": null et "proof_unaffected": true. "purged": false signifie que la suppression physique est en file d’attente.
401
unauthorized.
404
Aucun original en conservation.
Requête
curl -X DELETE https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/original \
  -H "Authorization: Bearer $SELLAT_API_TOKEN"
Réponse
{
  "proof_id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "original": null,
  "purged": true,
  "proof_unaffected": true
}

Compte

Votre compte

GET /account

Ce que peut faire la clé qui appelle : l’offre, les horodatages qu’il vous reste (bienvenue et packs), l’espace et les téléchargements de conservation, les preuves du jour et leur limite, et les clés actives. Consultez-le avant un gros lot.

Le bloc connector contient les limites des boutiques connectées (WooCommerce, bientôt disponible).

Réponses

200
Le compte.
401
unauthorized.
Requête
curl https://sellat.app/api/v2/account \
  -H "Authorization: Bearer $SELLAT_API_TOKEN"
Réponse
{
  "plan": "free",
  "plan_name": "Free",
  "seals": {
    "available": true,
    "remaining": 1,
    "welcome_remaining": 1,
    "pack_remaining": 0,
    "price_eur": 6,
    "buy_url": "https://sellat.app/precios"
  },
  "custody": {
    "available": true,
    "used_bytes": 0,
    "max_account_bytes": 52428800,
    "max_file_bytes": 10485760,
    "downloads_used_today": 0,
    "max_downloads_per_day": 10
  },
  "proofs": {
    "daily_limit": 10,
    "used_today": 3,
    "resets_in_seconds": 41231
  },
  "connector": {
    "mode_available": "daily",
    "eidas_available": false,
    "custody_available": false,
    "operations": {
      "daily_limit": 2000,
      "resets_in_seconds": 41231
    }
  },
  "keys": {
    "active": 1
  },
  "urls": {
    "dashboard": "https://sellat.app/dashboard",
    "plans": "https://sellat.app/precios#pricing-business",
    "seals": "https://sellat.app/precios#pricing-seal-title",
    "docs": "https://sellat.app/developers/docs",
    "connect": "https://sellat.app/connect/woocommerce"
  }
}

Preuve portable

Preuve portable (proof.json)

GET /proof/{id}.json Sans clé

Le document qui permet à chacun de vérifier la preuve sans Sellat, au format public sellat-proof/2 : l’empreinte, la feuille, le chemin Merkle, la racine, l’ancrage Polygon et les attestations (Bitcoin et l’horodatage qualifié). Il est public exprès : qui le détient peut vérifier la preuve sans compte.

Paramètres

id string · chemin obligatoire
L’id de la preuve.

Réponses

200
Le proof.json.
202
Pas encore ancrée : {"ready": false, "state": "..."} avec Retry-After: 60.
400
L’id n’est pas un UUID.
404
Elle n’existe pas.
Requête
curl https://sellat.app/api/v2/proof/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e.json -o proof.json
Réponse
{
  "schema": "sellat-proof/2",
  "proof_id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
  "content": {
    "algorithm": "SHA-256",
    "hash": "9f3c2a5e0b7d1c4f8a6e2d9b3c7f1a5e8d2c6b0f4a9e3d7c1b5f8a2e6d0c4b9f"
  },
  "leaf": {
    "formula": "SHA-256('sellat-leaf:v2:' + proof_id + ':' + content.hash)",
    "value": "31668b4d846680cd920a1cb5be200ab0562909e8778223087c1738746a1cbb26"
  },
  "merkle": {
    "index": 1,
    "path": [
      {
        "position": "left",
        "hash": "9e246a6d6dda91ee3916e149d834cd9a140e3470fbf5b6ec67824705dbbc82d4"
      }
    ],
    "root": "88fa1c4c3587d86d6f713e438c7ae745a91a6e03a5dd1b8282f17f35f30bfa14"
  },
  "anchors": [
    {
      "chain_id": 137,
      "network": "Polygon Mainnet",
      "tx_hash": "0xa4195d4ea808610dee92a1caa221d35f289e61674f451c82f7428e29813f9d6a",
      "block_number": 94653624,
      "payload": "sellat:v2:88fa1c4c3587d86d6f713e438c7ae745a91a6e03a5dd1b8282f17f35f30bfa14"
    }
  ],
  "attestations": [
    {
      "type": "opentimestamps",
      "state": "submitted"
    },
    {
      "type": "rfc3161-qualified-timestamp",
      "state": "issued"
    }
  ]
}

Il vous manque quelque chose, ou vous avez besoin de plus de volume ? Écrivez à [email protected] : vous parlerez avec ceux qui ont écrit le code.