This guide walks through signing an invoice with the Cloud Signature Consortium v2 API on CodeB Sovereign Communications, embedding an RFC 3161 qualified timestamp, and adding OCSP revocation data for long-term validity (PAdES-B-LT). Result: a PDF that meets the GoBD immutability requirement cryptographically.
Prerequisites. You need (1) an OIDC bearer access token for your CodeB tenant, (2) HTTPS reach to https://<tenant>/csc.ashx, (3) a PDF whose ByteRange you want to hash. For a production invoice archive you also need a configured QTSP as timestamp source (default: Sectigo).
1 Obtain an OIDC bearer access token
The CSC v2 endpoint authenticates callers via OIDC bearer. Two common flows:
Interactive (authorization_code) — for web applications with a signed-in user.
Client credentials — for server-to-server signing from accounting software.
Client credentials example (your tenant must have the grant enabled):
The response carries access_token, sent onward as Authorization: Bearer <token>.
Note: the acting sub in the token determines the signing identity — the certificate is populated from the OIDC profile with given / family / e-mail on first access.
Response contains cert.certificates[] (PEM), key.algo (1.2.840.10045.4.3.2 = ECDSA-SHA256), SCAL (1 for automatic authorisation, 2 for 2FA confirmation).
3 Compute the SHA-256 invoice hash
The endpoint signs a SHA-256 hash, not the PDF itself. For PAdES the hash covers the ByteRange of the PDF being signed — everything except the placeholder where the signature bytes go.
Node.js sample:
const crypto = require('crypto');
const fs = require('fs');
// Prepare the PDF: extract the ByteRange region
// (See sign.html client for full byte-range accounting.)
const pdfBytesToSign = getByteRangeBytes(pdfBuffer);
const hash = crypto.createHash('sha256').update(pdfBytesToSign).digest();
const hashB64 = hash.toString('base64');
For quick tests you can hash the whole file — the API will return a valid signature but the result is not a PAdES-conformant PDF signature. Use signatures/signDoc if you'd rather let the server assemble the PDF envelope.
4 Obtain the Signature Activation Data (SAD)
CSC v2 separates authorisation from execution. The authorisation step returns a short-lived SAD JWT that unlocks signing a specific hash by the linked user:
Response: {"SAD": "eyJ..."}. The SAD is a JWT with claims sub, credential_id, hash, exp. Typical validity 5 minutes.
SCAL2 accounts (2FA required): For SCAL2 credentials this endpoint returns an authorization_id instead, and you must confirm the second factor via credentials/authorize/confirm. See the API reference.
Response: {"signatures": ["MEUCIQ..."]} — base64-encoded CMS signature bytes that go into the PDF's /Sig dictionary.
Shortcut: signDoc instead of signHash
If your client would rather not construct the ByteRange itself, signatures/signDoc uploads the whole PDF and returns it signed. Convenient for small volumes; scales worse because every PDF travels the wire.
6 Embed the qualified RFC 3161 timestamp (PAdES-B-T)
To bind the signature to an officially attested moment, attach a qualified timestamp. CodeB proxies the QTSP:
The returned timestamp-token.tst is the RFC 3161 timestamp token, embedded as the id-aa-signatureTimeStampToken attribute on the CMS signature. Result: a PAdES-B-T signature.
Default QTSP is Sectigo (URL http://timestamp.sectigo.com/qualified). Configured via the Windows registry value HKLM\SOFTWARE\CodeB\TSAURL. Customers with their own QTSP (e.g. Maltese Gattaca) can point elsewhere.
7 Embed OCSP revocation data (PAdES-B-LT)
For the signature to remain verifiable ten years on, embed the revocation data as it was at signing time. Without this step, later verification tools would have to reach the original OCSP responder — which may no longer exist.
Response: {"ocsp_response": "MIIBoAoBAK..."}. Embed it as the id-aa-ets-revocationValues attribute on the CMS signature. Result: PAdES-B-LT — the recommended target for ten-year invoice archives.
8 Error handling
Errors are structured JSON in the RFC 6749 pattern:
{
"error": "invalid_request",
"error_description": "hash: must be 32 raw bytes (SHA-256), base64-encoded"
}
400 invalid_request — missing or wrong parameters. Usually the hash is not 32 bytes long or not base64-encoded.
400 only_sha256_supported — a hash algorithm other than SHA-256 was requested.
401 invalid_token — bearer expired or invalid. Fetch a new one.
403 https_required — the call came over HTTP. CSC v2 accepts HTTPS only.
413 body_too_large — body over 32 KiB (10 MiB for signDoc). Shrink the PDF or use signHash.
429 — rate limit exceeded (30 requests/min per action per IP). Response carries Retry-After: 30.
501 crypto_module_not_configured — signature module not configured on this tenant. Contact your admin.