REST · JSON · eIDAS QES · EUDI-READY · POST-QUANTUM READY
API Reference
Send documents, add signers, collect qualified signatures, and download sealed ASiC-E containers — everything in the Sign by Kodar dashboard is available over HTTPS.
EUDI Wallet-ready (eIDAS 2.0). Signing methods are negotiated server-side — EUDI Wallet first when available, falling back to Smart-ID and Mobile-ID automatically. Integrations built on this API today need no changes when EU Digital Identity Wallets roll out; optionally steer routing per signer with preferredMethods.
Authentication
Every request carries a bearer token. Keys are scoped per tenant — create and revoke them in the developer console. Keys are prefixed kdr_, shown once at creation, and stored only as a hash.
Keys grant signing authority. Never expose them client-side.
EXAMPLE REQUEST
curl https://api.sign.kodar.io/v1/documents \ -H "Authorization: Bearer kdr_..." \ -H "Content-Type: application/json"
Core endpoints
Signing lifecycle
Webhook payload
Set an endpoint in the console; we POST document.completed the moment the last signature lands.
{
"event": "document.completed",
"documentId": "7b07e940-…",
"data": {
"title": "Service agreement — Acme × Kodar",
"asicePath": "documents/…/7b07e940-….asice",
"signers": [
{ "name": "Mari Maasikas",
"signedAt": "2026-07-04T09:20:41Z" }
]
}
}Payloads are signed — verify the X-Kodar-Signature HMAC with your signing secret.
VERIFY (NODE)
import { createHmac, timingSafeEqual } from "crypto";
const expected = "sha256=" + createHmac("sha256", secret)
.update(rawBody).digest("hex");
const ok = timingSafeEqual(
Buffer.from(header), Buffer.from(expected));Errors
Consistent envelope: { error: { code, message } }
Rate limits
When exceeded you receive 429 with a Retry-After header. Signing sessions already in progress are never interrupted.
Download OpenAPI 3.1 spec ↓Endpoint details
/documentsCreate a document. Pass send: true to email signing links immediately — the response then carries a signing URL per signer.
REQUEST
curl https://api.sign.kodar.io/v1/documents \
-H "Authorization: Bearer kdr_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Service agreement — Acme × Kodar",
"pdfBase64": "JVBERi0xLjQK...",
"send": true,
"signers": [
{ "firstName": "Mari", "lastName": "Maasikas",
"idCode": "60001019906", "countryCode": "EE",
"email": "mari@acme.ee", "phoneNumber": "+37200000766" }
]
}'RESPONSE
201 Created
{
"id": "7b07e940-a80c-41aa-8179-eba37a59dc12",
"status": "pending_signatures",
"signingLinks": [
{ "signerId": "…", "email": "mari@acme.ee",
"signingUrl": "https://sign.kodar.io/s/eyJ…" }
]
}/documentsList your documents, newest first. Optional ?limit= (max 100).
REQUEST
curl https://api.sign.kodar.io/v1/documents?limit=10 \ -H "Authorization: Bearer kdr_..."
RESPONSE
200 OK
{ "documents": [
{ "id": "…", "title": "…", "status": "completed",
"sha256": "c025…f9a2", "createdAt": "2026-07-04T09:12:00Z" } ] }/documents/:idOne document with per-signer status. containerReady turns true once every signer has signed.
REQUEST
curl https://api.sign.kodar.io/v1/documents/7b07e940-… \ -H "Authorization: Bearer kdr_..."
RESPONSE
200 OK
{
"id": "7b07e940-…", "title": "…", "status": "pending_signatures",
"sha256": "c025…f9a2", "containerReady": false,
"signers": [
{ "id": "…", "name": "Mari Maasikas", "email": "mari@acme.ee",
"countryCode": "EE", "status": "signed",
"signedAt": "2026-07-04T09:20:41Z" } ]
}/documents/:id/sendSend a draft — or re-send invites. Re-sending mints fresh single-use links and invalidates previously issued ones.
REQUEST
curl -X POST https://api.sign.kodar.io/v1/documents/7b07e940-…/send \ -H "Authorization: Bearer kdr_..."
RESPONSE
200 OK
{ "id": "7b07e940-…", "status": "pending_signatures",
"signingLinks": [ { "signerId": "…", "email": "…", "signingUrl": "…" } ] }/documents/:id/containerDownload the sealed ASiC-E container. 409 not_ready until all signers have signed.
REQUEST
curl https://api.sign.kodar.io/v1/documents/7b07e940-…/container \ -H "Authorization: Bearer kdr_..." -o agreement.asice
RESPONSE
200 OK Content-Type: application/vnd.etsi.asic-e+zip Content-Disposition: attachment; filename="agreement.asice" <binary .asice bytes>
/algorithmsThe crypto-agility switchboard: every signature algorithm the engine supports — classical (RSA, ECDSA) and post-quantum (ML-DSA, NIST FIPS 204) — and whether this deployment can sign with it right now. Algorithms are registry entries, not hardcoded: when qualified post-quantum certificates arrive, they show up here and your integration doesn't change.
REQUEST
curl https://api.sign.kodar.io/v1/algorithms \ -H "Authorization: Bearer kdr_..."
RESPONSE
200 OK
{ "algorithms": [
{ "id": "ecdsa-sha256", "family": "EC", "displayName": "ECDSA + SHA-256",
"digest": "SHA-256", "status": "qualified", "enabled": true },
{ "id": "ml-dsa-65", "family": "ML-DSA", "displayName": "ML-DSA-65 (FIPS 204)",
"digest": "SHA-256", "status": "pqc-demo", "enabled": true },
… ] }/sealApply the organisation e-seal (Enterprise) to a PDF or an existing container. Pass algorithm: "ml-dsa-65" for the post-quantum demo seal: the content signature uses the NIST quantum-resistant standard, the qualified timestamp stays classical, and the container is a demo profile until QTSPs issue post-quantum certificates — validate it via POST /validate, which routes PQC containers to the in-house engine and reports interoperability: "pqc-demo".
REQUEST
curl https://api.sign.kodar.io/v1/seal \
-H "Authorization: Bearer kdr_..." \
-H "Content-Type: application/json" \
-d '{
"documentBase64": "JVBERi0xLjQK...",
"filename": "agreement.pdf",
"algorithm": "ml-dsa-65"
}'RESPONSE
200 OK
{ "valid": true, "errors": [],
"signatures": [
{ "signedBy": "CN=KODAR PQC DEMO SEAL, O=Kodar OU, C=EE",
"profile": "PQC-DEMO", "algorithm": "ML-DSA-65 (FIPS 204)",
"trustedSigningTime": "2026-07-05T10:12:00Z" } ],
"interoperability": "pqc-demo",
"asiceBase64": "UEsDBBQ..." }