API reference
Sellat API v2
Everything the web app does, from your own system: register a file’s SHA-256 fingerprint, anchor it on Polygon and Bitcoin, request the FNMT qualified timestamp, download the certificate and the evidence package, and keep the original in custody. REST and JSON, with an API key.
https://sellat.app/api/v2 Introduction
The API works with the file’s fingerprint, not the file: you compute the SHA-256 on your side and send only those 64 characters. The original never leaves your machine unless you choose to put it in custody.
Each proof goes into a Merkle batch anchored on Polygon, and the same batch is attested on Bitcoin. If you ask for it, the fingerprint also gets the qualified timestamp of FNMT-RCM, a qualified trust service provider on the EU trusted list.
Every route hangs off the base URL and speaks JSON. Proofs you create through the API and on the web belong to the same account and show up in the same dashboard.
It is meant to be called from a server: it sends no CORS headers, so a browser cannot call it from another site, and your key must never be in code that reaches a browser.
Authentication
Every request carries your key in the Authorization: Bearer sellat_... header. Keys are created and revoked from your dashboard; an account can hold up to 5 active keys, one per integration.
Without a key, or with a revoked one, the API answers 401 unauthorized with a WWW-Authenticate: Bearer header. The only public route is the portable proof (/proof/{id}.json).
curl https://sellat.app/api/v2/account \
-H "Authorization: Bearer sellat_3f9c..." Quick start
Compute the fingerprint, create the proof with the qualified seal and keep the id you get back. Within minutes it becomes anchored and all its URLs work.
Without "qualified": true the proof is just as verifiable (Polygon and Bitcoin) and spends no seal. You can request the seal later with POST /proofs/{id}/qualified.
# 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}" Proof states
The state field says what Sellat knows at each moment: it never reports a transaction as confirmed before it has checked it.
The Bitcoin attestation (bitcoin) runs separately: pending until OpenTimestamps includes it in a block, then confirmed with its height. The qualified seal (qualified) does not depend on the state: it is issued straight away.
| State | What it means |
|---|---|
queued | Received. Waiting to join a batch, about two minutes at most. |
batched | In a Merkle batch, with its path fixed. Waiting for the Polygon anchor. |
anchored | Anchored: the transaction is in a Polygon block. proof.json is available. |
confirmed | The block has 12 confirmations. |
Errors
An error is always JSON with error.code (stable, for your code) and error.message (for people). Some add fields, such as seals_remaining and buy_url on a 402. The portable proof, being public, answers {"error": "..."}.
If the gateway cuts a request before it reaches the API (too many requests from one IP, or a body over 25 MB), that 429 or 413 does not use this format.
| Code | HTTP | What happened |
|---|---|---|
invalid_request | 400 | A field is missing or not in the expected format. The message says which. |
unauthorized | 401 | No key, a malformed key or a revoked one. |
payment_required | 402 | No qualified seals left. Carries seals_remaining and buy_url. |
key_without_account | 403 | The key is not attached to an account (old keys). |
not_found | 404 | The proof does not exist or belongs to another account. |
conflict | 409 | Another account registered these bytes first, or the original is not stored yet. |
hash_mismatch | 409 | The file you upload does not have the proof’s fingerprint. |
file_too_large | 413 | The file exceeds your plan’s per-file limit (max_file_bytes). |
rate_limited | 429 | Too many requests per minute. Honour Retry-After. |
quota_exceeded | 429 | Daily proof limit reached. Retry-After runs to 00:00 UTC. |
download_quota_exceeded | 429 | Daily limit of original downloads reached. |
integrity_failed | 500 | The stored original no longer matches its fingerprint, so it is not served. |
unavailable | 502 · 503 | An internal service is not answering. Retry later. |
seal_unavailable | 503 | The timestamping authority is not available right now. |
custody_unavailable | 503 | Custody is not available right now. |
custody_quota_exceeded | 507 | No custody space left on your account. |
{
"error": {
"code": "payment_required",
"message": "No qualified seals left on this account.",
"seals_remaining": 0,
"buy_url": "https://sellat.app/precios"
}
} Limits
Each account can create a number of API proofs per day, across all its keys. Proofs made on the web do not count, and neither does sending bytes you already own again. The day resets at 00:00 UTC.
At the limit, POST /proofs answers 429 quota_exceeded with a quota object and Retry-After. Each route also has a per-minute brake (for example 60 creations and 10 seals a minute) that answers 429 rate_limited, and the gateway accepts up to 120 requests a minute per IP. A request body can be 25 MB at most.
Qualified seals have no daily limit: each one spends a seal from your balance. Check what you have left at any time with GET /account, and if you need more volume, write to [email protected].
| Free account | Pro account | |
|---|---|---|
| API proofs per day | 10 | 5,000 |
| Active keys | 5 | 5 |
| Original in custody, per file | 10 MB | 50 MB |
| Total custody | 50 MB | 5 GB |
| Original downloads per day | 10 | 50 |
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
}
}
} Idempotency
Send an Idempotency-Key header (up to 200 characters) with POST /proofs and you can retry safely: if you already used it with the same API key, you get the original proof back with 200 and "created": false, whatever the new body says, and no seal is issued.
Without the header, sending bytes your account already owns also returns the existing proof (200), without changing its name or metadata. But if another account registered those bytes first, every request creates a new proof with limitations: if you might retry, always use Idempotency-Key.
Seals are never duplicated either: a proof has one seal, and asking again returns the same one without spending another.
Verify without Sellat
A Sellat proof does not need Sellat to be checked. With the file and its proof.json, the open-source verifier recomputes the fingerprint and the Merkle path and reads the anchor on the public blockchain.
The qualified seal is checked with OpenSSL, using the file, the .tsr and the authority’s certificate, which comes inside the evidence package. -partial_chain is needed because the trusted list publishes the authority’s own certificate, not a CA’s.
# 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 Proofs
Create a proof
POST /proofs
Registers a file’s SHA-256 fingerprint and returns the proof. With "qualified": true it also issues the FNMT qualified seal in the same call, spending one seal from the account.
Parameters
-
hashrequired - The file’s SHA-256 fingerprint: 64 hexadecimal characters.
-
nameoptional - What the dashboard and the certificate call it. Only the file name is kept, not the path. Defaults to the first 12 characters of the hash.
-
metadataoptional - A JSON object of yours, up to 4 KB. Stored with the proof, never returned or published.
-
sizeoptional - File size in bytes. Informational.
-
mime_typeoptional - The file’s type, used when serving the original if you keep it in custody. Defaults to
application/octet-stream. -
qualifiedoptional -
truealso issues the FNMT qualified seal. -
Idempotency-Keyoptional - Retry without duplicating (see Idempotency).
Responses
- 201
- Proof created.
- 200
- It already existed: these bytes are already your account’s, or you repeated an
Idempotency-Key. Carries"created": false. - 400
invalid_request: a field is missing or invalid.- 401
unauthorized.- 402
payment_required: you asked for the seal and have none left. Nothing is written.- 409
conflict: you asked for the seal on bytes another account registered first.- 429
quota_exceeded(daily limit) orrate_limited(per minute).- 503
seal_unavailableorunavailable. If the proof was created, the error carries itsproof_id.
If the authority is slow to answer, the proof is still created and qualified comes back as {"state": "pending"}: ask again with POST /proofs/{id}/qualified, at no cost.
If another account registered those bytes first, the proof is created and anchored but has no certificate of its own: the certificate URLs are null and the response carries "limitations": ["hash_registered_by_another_account"].
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" }
}' {
"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"
}
} Retrieve a proof
GET /proofs/{id}
Returns the proof with its state, its anchors, the Bitcoin attestation, the seal if it has one, the original if it is in custody, and all its URLs.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
- The proof.
- 401
unauthorized.- 404
not_found: it does not exist or belongs to another account.
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e \
-H "Authorization: Bearer $SELLAT_API_TOKEN" {
"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"
}
} List proofs
GET /proofs
Your account’s proofs, newest first: those created through the API and those created on the web. Each item in data is a full proof, like GET /proofs/{id}.
For the next page, pass the next_cursor the previous one returned. When it is null, there are no more.
Parameters
-
limitoptional - Proofs per page, 1 to 100. Defaults to 25.
-
cursoroptional - The
next_cursorof the previous page.
Responses
- 200
dataandnext_cursor.- 400
invalid_request: invalidlimitorcursor.- 401
unauthorized.
curl "https://sellat.app/api/v2/proofs?limit=25" \
-H "Authorization: Bearer $SELLAT_API_TOKEN" {
"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"
} Qualified seal (eIDAS)
Request the qualified seal
POST /proofs/{id}/qualified
Issues an RFC 3161 timestamp from FNMT-RCM over the file’s fingerprint. Under Article 41(2) of the eIDAS Regulation, a qualified electronic timestamp enjoys the presumption of the accuracy of the date and time it indicates and of the integrity of the data.
It spends one seal from the account: the welcome seal or one from your packs. The API never charges a card or opens a payment; packs are bought on the pricing page.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
- Seal issued, or it already had one (
"created": false, nothing spent). Carriesseals_remaining. - 202
- The seal is paid for but the authority did not answer. Call again (
Retry-After: 30): it completes at no cost. - 401
unauthorized.- 402
payment_required: no seals left. Carriesseals_remainingandbuy_url.- 404
not_found.- 409
conflict: another account registered these bytes first.- 503
seal_unavailable.
A proof has one seal, forever. If issuance fails for good, the seal goes back to your balance.
curl -X POST https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified \
-H "Authorization: Bearer $SELLAT_API_TOKEN" {
"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
} Retrieve the seal
GET /proofs/{id}/qualified
Returns only the proof’s seal, or null if it has none yet. It issues nothing and spends nothing.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
proof_idandqualified.- 401
unauthorized.- 404
not_found.
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified \
-H "Authorization: Bearer $SELLAT_API_TOKEN" {
"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"
}
} Download the token (.tsr)
GET /proofs/{id}/qualified.tsr
The seal exactly as the authority returned it: an RFC 3161 TimeStampResp in DER. It is the file an expert or a court can validate without Sellat.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
- The
.tsr(application/timestamp-reply). - 401
unauthorized.- 404
not_found: the proof does not exist or has no seal yet.
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/qualified.tsr \
-H "Authorization: Bearer $SELLAT_API_TOKEN" \
-o seal.tsr 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> Certificate and evidence
PDF certificate
GET /proofs/{id}/certificate.pdf
The proof’s certificate, for people: the fingerprint, the date, the anchors and the qualified seal if there is one, with instructions to verify it.
Parameters
-
idrequired - The proof’s
id. -
langoptional - Certificate language:
es,en,deorfr. Any other value givesen.
Responses
- 200
- The PDF.
- 401
unauthorized.- 404
not_found.- 409
conflict: another account registered these bytes first and the proof has no certificate of its own.
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 HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="certificado-3b1d7e42-9c5a-4f0e-b8d6-1a2c3e4f5a6b.pdf"
<PDF> Evidence package
GET /proofs/{id}/evidence.zip
A ZIP with what it takes to defend the proof without Sellat: the certificate, proof.json, the seal and the authority’s certificate if there is one, the instructions and, if it is in custody, the original.
Parameters
-
idrequired - The proof’s
id. -
langoptional - Language of the package’s texts:
es,en,deorfr.
Responses
- 200
- The ZIP.
- 401
unauthorized.- 404
not_found.- 409
conflict: another account registered these bytes first.
Including the original spends one of the day’s downloads. If none are left, the package comes without it and the README says so.
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 HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="sellat-evidencia-3b1d7e42-9c5a-4f0e-b8d6-1a2c3e4f5a6b.zip"
<ZIP> Custody of the original
Deposit the original
PUT /proofs/{id}/original
Stores the file next to its proof, on servers in the European Union. Sellat recomputes the fingerprint of what it receives and refuses it if it does not match the proof.
Send the raw bytes with the file’s Content-Type, or multipart/form-data under the file field. POST works the same as PUT.
Parameters
-
idrequired - The proof’s
id. -
filerequired - The file. Up to 10 MB on the free account and 50 MB on the Pro account, and 25 MB per request at most.
Responses
- 201
- Stored. Returns the proof with
original. - 200
- It was already stored.
- 400
invalid_request: the body is empty or unreadable.- 401
unauthorized.- 404
not_found.- 409
hash_mismatch(not the proof’s file) orconflict(another account’s bytes).- 413
file_too_large: over your plan’s per-file limit. Carriesmax_file_bytes.- 502
unavailable: storage did not answer.- 503
custody_unavailable.- 507
custody_quota_exceeded: no space left on your account. Carriesused_bytesandmax_account_bytes.
Custody is optional: the proof is just as valid without the original. Keeping it adds preservation; it does not condition the proof.
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 {
"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"
}
} Download the original
GET /proofs/{id}/original
Returns the file in custody. Sellat recomputes its fingerprint on the way out and refuses to serve it if it no longer matches. Each download counts towards your plan’s daily limit; the X-Sellat-Custody-Downloads-Remaining header says how many are left.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
- The file, with its type and name.
- 401
unauthorized.- 404
- No original in custody.
- 409
conflict: the original is not stored yet.- 429
download_quota_exceeded: daily download limit. Resets at 00:00 UTC.- 500
integrity_failed: what is stored no longer matches the fingerprint.- 502
unavailable.
curl https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/original \
-H "Authorization: Bearer $SELLAT_API_TOKEN" \
-o contract.pdf HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="contract.pdf"
X-Sellat-Custody-Downloads-Remaining: 9
<the original bytes> End custody
DELETE /proofs/{id}/original
Deletes the original in custody. The proof does not change: it stays anchored, sealed and verifiable.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
"original": nulland"proof_unaffected": true."purged": falsemeans physical deletion is queued.- 401
unauthorized.- 404
- No original in custody.
curl -X DELETE https://sellat.app/api/v2/proofs/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e/original \
-H "Authorization: Bearer $SELLAT_API_TOKEN" {
"proof_id": "6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e",
"original": null,
"purged": true,
"proof_unaffected": true
} Account
Your account
GET /account
What the calling key can do: the plan, the seals you have left (welcome and packs), custody space and downloads, today’s proofs and their limit, and the active keys. Check it before a large batch.
The connector block holds the limits for connected shops (WooCommerce, coming soon).
Responses
- 200
- The account.
- 401
unauthorized.
curl https://sellat.app/api/v2/account \
-H "Authorization: Bearer $SELLAT_API_TOKEN" {
"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"
}
} Portable proof
Portable proof (proof.json)
GET /proof/{id}.json No key
The document that lets anyone verify the proof without Sellat, in the public sellat-proof/2 format: the fingerprint, the leaf, the Merkle path, the root, the Polygon anchor and the attestations (Bitcoin and the qualified seal). It is public on purpose: whoever holds it can check the proof without an account.
Parameters
-
idrequired - The proof’s
id.
Responses
- 200
- The
proof.json. - 202
- Not anchored yet:
{"ready": false, "state": "..."}withRetry-After: 60. - 400
- The
idis not a UUID. - 404
- It does not exist.
curl https://sellat.app/api/v2/proof/6f2c9b1e-0d1a-4b9f-8e7c-2a4d5b6c7d8e.json -o proof.json {
"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"
}
]
} Missing something, or need more volume? Write to [email protected]: you will talk to the people who wrote the code.