API Reference
REST API for programmatic timestamping — use your API key from Settings → API Keys
Authentication
/auth/registerpublicCreate a new user account and a new organisation tenant. Existing organisations are joined through an invitation or verified identity-provider flow, never by supplying a matching email domain at signup.
Request body
{
"name": "string",
"email": "string",
"password": "string (min 8)",
"organisationName": "string (optional)"
}Response 200
{
"token": "<jwt>",
"userId": "uuid",
"email": "string",
"tenantId": "uuid",
"role": "owner"
}/auth/loginpublicAuthenticate and receive a JWT.
Request body
{
"email": "string",
"password": "string"
}Response 200
{
"token": "<jwt>",
"userId": "uuid",
"email": "string",
"tenantId": "uuid|null"
}/auth/refreshJWTRe-issue a JWT with the latest tenant context. Call after creating an organisation.
Response 200
{
"token": "<new jwt>"
}/auth/meJWTReturn the current user's profile decoded from the JWT.
Timestamping
Timestamp and verification endpoints accept digests and TSR evidence only. Deprecated full-file stamp/verify and raw TSA proxy endpoints have been removed.
/tsa/stamp-hashJWT / API keyTimestamp a document by its hash. Compute SHA-256 (or SHA-384 / SHA-512) of the file locally — only the digest is sent to Sigill. The file never leaves your machine. This is how RFC 3161 is meant to work.
Request body
{
"hashHex": "hex digest — SHA-256 (64 chars), SHA-384 (96), or SHA-512 (128)",
"tsaSlug": "qualified=false: auto | byot:<id-or-slug>; qualified=true: qualified option where available",
"label": "string (optional)",
"force": "boolean — re-stamp an already-stamped hash (default false)",
"qualified": "boolean — request an eIDAS-qualified timestamp (default false)"
}Response 200
{
"serial": "string",
"genTime": "ISO 8601",
"hashAlgorithmOid": "OID string",
"hashHex": "hex string",
"tsrBase64": "base64 .tsr token",
"tsaName": "string — timestamp token issuer metadata or customer authority label",
"qualified": "boolean",
"policyOid": "string | null — present when qualified: true"
}tsaSlug is "auto". Sigill may use multiple timestamp sources internally for reliability and failover; customers do not select individual Sigill-managed standard sources. Standard mode never routes to an eIDAS qualified option unless qualified: true is explicitly sent, and a standard response carrying a qualified policy is rejected rather than returned or stored. See Automatic TSA selection. Use force: true to create a second independent proof for a file that has already been stamped.You can also point
tsaSlug at one of your verified tenant-private BYOT references from Timestamp Authorities when you need to use a customer-managed TSA endpoint.Qualified stamps (
qualified: true) route exclusively to eIDAS Qualified Trust Service Providers on the EU Trust List. Qualified stamps are counted separately: lower plans receive three lifetime trial credits, while Business and Scale use their monthly qualified quota. Response 502 — all TSAs failed (auto)
{
"message": "All Sigill-managed timestamp sources failed.",
"attemptsTried": 3,
"failures": [
{
"source": "standard-pool-1",
"errorClass": "timeout",
"statusCode": null,
"message": "Request timed out after 10s",
"latencyMs": 10042
}
]
}Response 402 — quota exceeded
{
"message": "Monthly stamp limit of 50 reached. Upgrade your plan to continue."
}/tsa/restampJWT / API keyArchival restamp per RFC 3161 §4. Hashes the existing TSR bytes (not the original file) and stamps them with standard RFC 3161 timestamping, creating a timestamp chain. Responses carrying a qualified policy are rejected on this standard-only route. Use before a timestamp authority certificate expires.
Request body
{
"transactionId": "uuid — the record to restamp",
"tsaSlug": "auto | byot:<id-or-slug>"
}Response 200
{
"id": "uuid",
"parentTransactionId": "uuid",
"tsaName": "string",
"genTime": "ISO 8601"
}tsaSlug: "auto" for Sigill-managed standard timestamping. Use a verified BYOT timestamp authority when you need to restamp through a customer-managed TSA endpoint. /tsa/verify-hashpublicVerify a file against a TSR by its hash. The server validates the TSR token signature and timestamping certificate usage as well as the hash match. Call /tsa/inspect first to find the recorded hash algorithm, then compute that hash locally. The file never leaves your machine.
Request body
{
"fileHashHex": "hex digest of the file (algorithm from /tsa/inspect)",
"tsrBase64": "base64 .tsr token"
}Response 200
{
"valid": true,
"message": "File matches the timestamp.",
"details": {
"serial": "string",
"genTime": "ISO 8601",
"hashAlgorithmOid": "string",
"claimedHashHex": "string",
"providedHashHex": "string",
"hashMatch": true,
"tokenSignatureValid": true
}
}/tsa/inspectpublicParse and return the contents of a TSR token without verifying against a file.
Request body
{
"tsrBase64": "base64 .tsr token"
}Response 200
{
"genTime": "ISO 8601",
"serial": "string",
"hashAlg": "OID string",
"tsaName": "string",
"certNotBefore": "ISO 8601",
"certNotAfter": "ISO 8601"
}Automatic TSA selection
Pass tsaSlug: "auto" to /tsa/stamp-hash or /tsa/restamp for Sigill-managed standard RFC 3161 timestamping. Seal operations with qualified: false via /seal/sign use the same path; passing qualified: true embeds a qualified RFC 3161 timestamp inside the seal — the seal itself remains an advanced electronic seal (AdES) regardless. Verified tenant-private BYOT authorities may be selected explicitly when you need to use a customer-managed endpoint.
How it works
- Managed standard pool. Sigill may use multiple timestamp sources internally for reliability and failover. The exact internal source is not customer-selectable unless you configure BYOT.
- Failover on the same request. If an internal source fails, Sigill can try another source before returning an error. The caller only sees the final outcome.
- BYOT stays customer-managed. Verified tenant-private timestamp authorities are selected by their BYOT reference. If one returns a token carrying a qualified policy, standard mode rejects that response.
- Every internal failure is logged. Errors are stored with classification (
network,timeout,http_status,parse) for Sigill operations and backoffice health monitoring.
When to use auto
Default to auto in production. Use a verified BYOT timestamp authority when you need to use a customer-managed TSA endpoint.
Error response when all TSAs fail
If every Sigill-managed standard source fails within the same request, you get a 502 Bad Gateway with anonymized failure details:
{
"message": "All Sigill-managed timestamp sources failed.",
"attemptsTried": 3,
"failures": [
{
"source": "standard-pool-1",
"errorClass": "timeout",
"statusCode": null,
"message": "Request timed out after 10s",
"latencyMs": 10042
},
{
"source": "standard-pool-2",
"errorClass": "http_status",
"statusCode": 503,
"message": "service unavailable",
"latencyMs": 412
},
{
"source": "standard-pool-3",
"errorClass": "parse",
"statusCode": 200,
"message": "malformed TSR: unexpected ASN.1 tag",
"latencyMs": 980
}
]
}errorClass values are stable strings you can match in client code: network (connection refused, DNS, TLS), timeout (HTTP client timeout), http_status (TSA responded non-2xx), parse (response wasn't a valid TSR), unexpected_qualified (a standard candidate returned a qualified-policy token), unknown (catch-all). Lookup
/api/lookup/{hash}publicCheck if a file hash has been stamped. The hash must be a SHA-512 hex string. This endpoint is public — no authentication required.
Path parameter
hash SHA-512 hex string (128 chars)Response 200
{
"found": true,
"count": 2,
"latest": {
"id": "uuid",
"hash": "hex string",
"alg": "OID string",
"tsaName": "string",
"genTime": "ISO 8601",
"certNotAfter": "ISO 8601",
"hasTsr": true
},
"records": [
"same public cryptographic fields"
]
}Timestamps
/api/transactions/detailsJWT / API keyPaginated list of timestamp records for a tenant, with optional search. Each item includes hasRestamp so clients can identify records whose archival renewal chain has already advanced.
Query parameters
tenantId uuid (required)
page integer (default 1)
pageSize integer (default 50, max 200)
search string — filters on label or hash prefix (optional)Response 200
{
"total": 42,
"page": 1,
"pageSize": 50,
"items": [
{
"id": "uuid",
"parentTransactionId": null,
"isRestamp": false,
"hasRestamp": true,
"certNotAfter": "ISO 8601"
}
]
}/api/transactions/{id}/labelJWT / API keySet or update the label on a timestamp record.
Request body
{
"label": "string (max 255)"
}Response 200
{
"id": "uuid",
"label": "string"
}Timestamp Authorities
/proxy/servicesJWT / API keyList customer-selectable timestamping options available to the caller's tenant. Sigill-managed standard RFC 3161 timestamping is selected with tsaSlug: "auto" and internal standard sources are not returned here. Verified tenant-private BYOT authorities are returned as source: "tenant" entries; qualified timestamping options may appear as source: "qualified" when entitled.
Response 200
[
{
"id": "uuid",
"name": "My timestamp authority",
"proxySlug": "byot:my-tsa",
"preferredHashOid": "2.16.840.1.101.3.4.2.3",
"isQualified": false,
"source": "tenant"
},
{
"id": "uuid",
"name": "Qualified timestamping",
"proxySlug": "qualified",
"preferredHashOid": "2.16.840.1.101.3.4.2.3",
"isQualified": true,
"source": "qualified"
}
]Timestamp Authorities
Paid-plan tenants can add their own RFC 3161 endpoints under Settings → Timestamp authorities. Sigill verifies each configuration server-side before saving it, protects stored credentials with Data Protection, and exposes verified entries as tenant-private BYOT references.
/api/tenant/tsaJWTList the authenticated tenant's BYO TSA configurations. Returns the verification status and the last successful test result for each row.
Response 200
{
"planAllows": true,
"plan": "starter",
"maxPerTenant": 10,
"items": [
{
"id": "uuid",
"label": "My TSA",
"slug": "my-tsa",
"endpointUrl": "https://tsa.example.com",
"preferredHashOid": "2.16.840.1.101.3.4.2.3",
"authType": "basic",
"username": "tsa-user",
"hasSecret": true,
"isActive": true,
"verifiedAt": "ISO 8601",
"lastVerifySerialHex": "string",
"lastVerifyTsaName": "string"
}
]
}/api/tenant/tsa/verifyJWTRun a one-shot RFC 3161 probe against a candidate endpoint without saving it. This is the same server-side verification step used by Save.
Request body
{
"endpointUrl": "https://tsa.example.com",
"preferredHashOid": "2.16.840.1.101.3.4.2.3",
"authType": "none | basic | bearer",
"username": "string (basic only)",
"secret": "string (password or bearer token)"
}Response 200
{
"ok": true,
"serial": "string",
"genTime": "ISO 8601",
"hashAlgorithmOid": "OID string",
"tsaName": "string",
"lotlQualified": false
}/api/tenant/tsaJWTCreate a BYO TSA after the server verifies it. Free-plan tenants get a 402. The configuration is saved only after the endpoint passes the live RFC 3161 check.
Request body
{
"label": "My timestamp authority",
"slug": "my-tsa",
"endpointUrl": "https://tsa.example.com",
"preferredHashOid": "2.16.840.1.101.3.4.2.3",
"authType": "none | basic | bearer",
"username": "string (basic only)",
"secret": "string (password or bearer token)",
"responsibilityConfirmed": true
}/api/tenant/tsa/:idJWTUpdate a BYO TSA. Transport changes trigger a fresh verification before the row is committed.
/api/tenant/tsa/:idJWTRemove a BYO TSA. This remains available even after a plan downgrade so tenants can clean up their configuration.
tsaSlug values using their slug or byot:<slug>. BYOT endpoints are customer-managed; Sigill verifies technical RFC 3161 compatibility, not the customer's right to use the endpoint. Document Seal
Cryptographic seals backed by KMS — the private key never leaves. An RFC 3161 timestamp is embedded in every seal automatically. Owner role required to manage certificates; any authenticated user can seal.
Two signing paths are available depending on whether you need PDF embedding or privacy-preserving signing:
POST /seal/sign— uploads the full file. PDFs receive a PAdES signature (ETSI EN 319 142-1) embedded in the file. Primarily intended for PDF sealing.POST /seal/sign-hash— accepts a pre-computed SHA-256 digest only. Non-PDF files (e.g. JSONL, XML, plain text) receive a detached CAdES.p7swithout the original content ever leaving the client. Same privacy model as/tsa/stamp-hash.
/seal/certificatesJWT / API keyList active signing certificates for the tenant. The Sigill platform certificate is always appended at the end with source: "platform".
Response 200
[
{
"id": "uuid",
"label": "string",
"status": "active | provisioning | revoked",
"source": "byoc | platform",
"certSubject": "CN=...",
"certNotBefore": "ISO 8601",
"certNotAfter": "ISO 8601"
}
]/seal/certificatesJWT (owner)Provision a new signing key. Generates RSA-4096 in KMS and returns a PKCS#10 CSR for CA submission. Submit the CSR to a CA, then activate via the endpoint below.
Request body
{
"commonName": "Acme Corp Seal 2026",
"organization": "Acme Corp AS",
"countryCode": "NO",
"organizationalUnit": "string (opt)",
"locality": "string (opt)",
"state": "string (opt)",
"label": "string (opt)"
}Response 200
{
"id": "uuid",
"label": "string",
"status": "provisioning",
"kmsKeyArn": "arn:aws:kms:...",
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----..."
}/seal/certificates/:id/activateJWT (owner)Upload the CA-signed certificate chain PEM. Validates that the public key matches the provisioned KMS key before activating.
Request body
{
"certificatePem": "PEM certificate chain: leaf certificate plus optional intermediates"
}Response 200
{
"id": "uuid",
"status": "active",
"certSubject": "CN=...",
"certNotBefore": "ISO 8601",
"certNotAfter": "ISO 8601"
}/seal/certificates/:idJWT (owner)Revoke a certificate and schedule the KMS key for deletion (7-day grace period). Cannot be undone. Platform certificate cannot be revoked via this endpoint.
Response 200
{
"message": "Certificate revoked. KMS key deletion scheduled."
}/seal/signJWT / API keySeal a file by uploading it. Accepts multipart/form-data. File type is detected server-side from magic bytes — PDFs produce a PAdES-signed PDF (application/pdf); other files produce a detached CAdES .p7s (application/pkcs7-signature). For non-PDF CAdES, pqc: true adds an ML-DSA-87 signer in the same CMS. For non-PDF files where privacy matters, prefer POST /seal/sign-hash instead — it never receives the file content.
Form fields
{
"file": "file to seal (required)",
"certificateId": "uuid (required)",
"label": "string — stored in operation log (opt)",
"reason": "PDF only — written into PDF /Reason field (opt)",
"location": "PDF only — written into PDF /Location field (opt)",
"qualified": "boolean — embed a qualified RFC 3161 timestamp (default false)",
"pqc": "boolean — CAdES only, add ML-DSA-87 signer (default false)"
}Response headers
{
"X-Seal-Operation-Id": "uuid",
"X-Seal-Certificate-Id": "uuid",
"X-Seal-Timestamped-By": "TSA name | none",
"X-Seal-Format": "pades-b-lta | pades-b-lt | pades-b-t | pades-bes | cades-x-l | cades-t | cades-bes",
"X-Seal-Qualified": "true | false",
"X-Seal-Pqc": "ml-dsa-87 | none"
}application/pdf), save as filename_sealed.pdf.Non-PDF response — detached CAdES signature (
application/pkcs7-signature), save as filename.p7s alongside the original file.If every Sigill-managed TSA fails, the seal is still produced but
X-Seal-Timestamped-By is none and the format degrades to *-bes. /seal/sign-hashJWT / API keySeal a non-PDF file by sending only its digest. The original file content never leaves the client — same privacy model as /tsa/stamp-hash. For pqc: true, send both SHA-256 (hashHex) for the classical signer and SHA-512 (hashHex512) for the ML-DSA signer. Returns a detached CAdES .p7s that verifies against the original file.
Request body (JSON)
{
"hashHex": "64-char lowercase SHA-256 hex of the file (required)",
"certificateId": "uuid (required)",
"label": "string — stored in operation log (opt)",
"qualified": "boolean — embed a qualified RFC 3161 timestamp (default false)",
"pqc": "boolean — add ML-DSA-87 signer (default false)",
"hashHex512": "128-char SHA-512 hex of same file; required when pqc=true"
}Response headers
{
"X-Seal-Operation-Id": "uuid",
"X-Seal-Certificate-Id": "uuid",
"X-Seal-Timestamped-By": "TSA name | none",
"X-Seal-Format": "cades-x-l | cades-t | cades-bes",
"X-Seal-Qualified": "true | false",
"X-Seal-Pqc": "ml-dsa-87 | none"
}.p7s (application/pkcs7-signature). Save it alongside the original file. To verify, supply both the original file and the .p7s to POST /seal/verify — the verifier recomputes the hash and checks it matches the digest embedded in the signature.400 if
hashHex is not a valid 64-character hex string. PAdES (PDF) sealing is not supported on this endpoint — use /seal/sign for PDFs. /seal/operationsJWT / API keyPaginated seal history for the authenticated tenant.
Query params
{
"page": 1,
"pageSize": 50,
"search": "label prefix or hash prefix (opt)"
}Response 200
{
"total": 42,
"page": 1,
"pageSize": 50,
"items": [
{
"id": "uuid",
"documentHash": "hex",
"label": "string",
"status": "success",
"createdAt": "ISO 8601",
"certLabel": "string",
"signatureType": "pades | cades",
"hasP7s": "boolean — true when .p7s is stored and downloadable",
"tsaName": "string | null"
}
]
}/seal/operations/:id/p7sJWT / API keyDownload the stored CAdES .p7s for a seal operation. Only available when signatureType is "cades" and the tenant has Store CAdES seals enabled in Settings. Returns application/pkcs7-signature.
Response 200
.p7s file bytes (binary)Response 404
{
"message": ".p7s not stored — enable Store CAdES seals in Settings, or download immediately after sealing."
}/seal/verifypublicVerify a sealed document. Accepts multipart/form-data. Routes automatically — if a p7s field is present, performs CAdES verification against the original file; otherwise treats file as a sealed PDF and performs PAdES verification.
Form fields
{
"file": "original file or sealed PDF (required)",
"p7s": ".p7s detached signature — CAdES path only (opt)",
"tsr": "standalone .tsr token for external timestamp verification (opt)"
}Response 200 — PAdES
{
"format": "pades",
"pades": {
"signaturePresent": true,
"hashMatch": true,
"certificate": {
"subject": "CN=Acme Corp Seal",
"trust": "chained | dev_ca | self_signed",
"qc": {
"isEidasQualified": false
}
},
"timestamp": {
"genTime": "ISO 8601",
"tsaName": "string",
"qc": {
"isEidasQualified": true
}
}
}
}Response 200 — CAdES
{
"format": "cades",
"cades": {
"signaturePresent": true,
"hashMatch": true,
"fileHashHex": "hex",
"certificate": {
"subject": "CN=Acme Corp Seal",
"trust": "chained"
},
"timestamp": {
"genTime": "ISO 8601",
"tsaName": "string"
},
"postQuantum": {
"present": true,
"valid": true,
"algorithm": "ml-dsa-87",
"signatureValid": true,
"contentBound": "yes | no | not_checked",
"trusted": "yes | no | not_evaluated"
},
"tsrSource": "embedded | external | null",
"tsrMatchError": null,
"error": null
}
}Error responses
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"detail": "No file provided."
}/seal/verify-hashpublicVerify a detached CAdES seal using document hashes only. For hybrid PQC seals, include hashHex512 to check the ML-DSA signer's SHA-512 content binding. Without it, the PQC signature can still be checked but postQuantum.contentBound is "not_checked".
Request body
{
"hashHex": "SHA-256/384/512 hex for the classical signer",
"hashHex512": "128-char SHA-512 hex for PQC binding (optional)",
"p7sBase64": "base64 .p7s",
"tsrBase64": "base64 standalone TSR (optional)"
}Response 200
{
"format": "cades",
"cades": {
"signaturePresent": true,
"hashMatch": true,
"signatureValid": true,
"postQuantum": {
"present": true,
"valid": false,
"algorithm": "ml-dsa-87",
"signatureValid": true,
"contentBound": "not_checked",
"trusted": "no | not_evaluated"
}
}
}Code examples
Stamp a file with curl
# 1. Hash your file locally — the file never leaves your machine
HASH=$(sha256sum yourfile.pdf | awk '{print $1}')
# 2. Stamp the hash — "auto" uses Sigill-managed standard
# RFC 3161 timestamping with redundancy and failover.
curl -X POST https://api.sigill.ai/tsa/stamp-hash \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"hashHex": "'"$HASH"'",
"tsaSlug": "auto",
"label": "yourfile.pdf"
}' | jq .
# 3. Save the .tsr token
curl -X POST https://api.sigill.ai/tsa/stamp-hash \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"hashHex":"'"$HASH"'","tsaSlug":"auto"}' \
| jq -r .tsrBase64 | base64 -d > yourfile.tsrStamp a file with Python
import hashlib, base64, httpx
API_KEY = "your_api_key"
BASE_URL = "https://api.sigill.ai"
# Hash the file locally — it never leaves your machine
with open("yourfile.pdf", "rb") as f:
hash_hex = hashlib.sha256(f.read()).hexdigest()
resp = httpx.post(
f"{BASE_URL}/tsa/stamp-hash",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
# "auto" uses Sigill-managed standard RFC 3161 timestamping.
# Use a verified BYOT reference when you need a customer-managed TSA endpoint.
"hashHex": hash_hex,
"tsaSlug": "auto",
"label": "yourfile.pdf",
},
)
resp.raise_for_status()
data = resp.json()
# Save the .tsr token
tsr_bytes = base64.b64decode(data["tsrBase64"])
with open("yourfile.tsr", "wb") as f:
f.write(tsr_bytes)
# data["tsaName"] reports timestamp token issuer metadata or your BYOT label
print(f"Stamped at {data['genTime']} by {data['tsaName']}")Stamp a file with Node.js
import fs from "fs";
import crypto from "crypto";
import fetch from "node-fetch";
const API_KEY = "your_api_key";
const BASE_URL = "https://api.sigill.ai";
// Hash the file locally — it never leaves your machine
const hashHex = crypto
.createHash("sha256")
.update(fs.readFileSync("yourfile.pdf"))
.digest("hex");
const res = await fetch(`${BASE_URL}/tsa/stamp-hash`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
// "auto" = Sigill-managed standard timestamping with failover. Recommended for production.
hashHex,
tsaSlug: "auto",
label: "yourfile.pdf",
}),
});
if (res.status === 502) {
// All TSAs failed — inspect the structured failure list
const err = await res.json();
console.error(`${err.attemptsTried} TSA(s) failed:`, err.failures);
process.exit(1);
}
const data = await res.json();
// Save the .tsr token
const tsr = Buffer.from(data.tsrBase64, "base64");
fs.writeFileSync("yourfile.tsr", tsr);
console.log(`Stamped at ${data.genTime} by ${data.tsaName}`);Seal a non-PDF file (CAdES) with curl
For PDFs the response is a signed PDF; for any other file you get a .p7s detached signature. Check X-Seal-Format to branch.
# Seal any file — PDF gets PAdES, everything else gets CAdES .p7s
CERT_ID="<your-certificate-uuid>"
FILE="report.json"
RESPONSE=$(curl -s -D - -o /tmp/sealed_output -X POST https://api.sigill.ai/seal/sign -H "Authorization: Bearer YOUR_API_KEY" -F "file=@$FILE" -F "certificateId=$CERT_ID" -F "label=$FILE" -F "qualified=false")
FORMAT=$(echo "$RESPONSE" | grep -i "x-seal-format:" | tr -d '
' | awk '{print $2}')
if [ "$FORMAT" = "cades" ]; then
# Non-PDF: response is a detached .p7s signature
# Keep both the original file and the .p7s — you need both to verify
mv /tmp/sealed_output "${FILE%.json}.p7s"
echo "CAdES seal saved: ${FILE%.json}.p7s"
echo "Keep alongside original: $FILE"
else
# PDF: response is the sealed PDF with embedded signature
mv /tmp/sealed_output "${FILE%.pdf}_sealed.pdf"
echo "PAdES seal saved: ${FILE%.pdf}_sealed.pdf"
fiSeal a non-PDF file (CAdES) with Python
import httpx, pathlib
API_KEY = "your_api_key"
BASE_URL = "https://api.sigill.ai"
CERT_ID = "<your-certificate-uuid>"
file_path = pathlib.Path("report.json")
with open(file_path, "rb") as fh:
resp = httpx.post(
f"{BASE_URL}/seal/sign",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": (file_path.name, fh, "application/octet-stream")},
data={
"certificateId": CERT_ID,
"label": file_path.name,
"qualified": "false",
},
timeout=60,
)
resp.raise_for_status()
fmt = resp.headers.get("x-seal-format", "pades")
if fmt == "cades":
# Non-PDF: detached CAdES .p7s — store alongside the original file
out = file_path.with_suffix(".p7s")
out.write_bytes(resp.content)
print(f"CAdES seal → {out}")
print(f"Keep original file: {file_path}")
print(f"Verify: POST /seal/verify with file={file_path.name} + p7s={out.name}")
else:
# PDF: sealed PDF with embedded PAdES signature
out = file_path.with_stem(file_path.stem + "_sealed").with_suffix(".pdf")
out.write_bytes(resp.content)
print(f"PAdES seal → {out}")Verify a CAdES seal with curl
# Verify a CAdES seal — pass original file + .p7s
# The server checks the signature and returns structured JSON
curl -s -X POST https://api.sigill.ai/seal/verify \
-F "file=@report.json" \
-F "p7s=@report.p7s" \
| jq '{
format: .format,
intact: .cades.hashMatch,
signer: .cades.certificate.subject,
timestamp: .cades.timestamp.genTime,
tsa: .cades.timestamp.tsaName,
qualified: .cades.timestamp.qc.isEidasQualified
}'
# To also verify the embedded timestamp against the standalone .tsr:
curl -s -X POST https://api.sigill.ai/seal/verify \
-F "file=@report.json" \
-F "p7s=@report.p7s" \
-F "tsr=@report.tsr" \
| jq '.cades.tsrSource' # → "external" when .tsr matched