Nexiel Sign quickstart

Nexiel Sign issues a Qualified Electronic Signature (QES, eIDAS Art. 3(12)) on a PDF document, attributing it to one specific natural person. It lives on Nexiel Screen as a single route, POST /v1/sign, not a new service and not a wallet session flow: send a document, get back a signed one.

There is no sandbox mode for this route, and no example below runs against a live QTSP. A QES is a real legal act with real consequences for the signer, so POST /v1/sign requires a real Keycloak-authenticated bearer token in every service mode, including a self-run sandbox instance. See production status below for the real, current gap this is waiting on.

What this is, and what this isn't

Nexiel Screen also issues a Qualified Electronic Seal (QSeal, eIDAS Art. 3(25)) at POST /v1/seal. A QSeal attributes a document to a legal person, the company itself, using that company's own single, pre-provisioned credential. A QES attributes a document to a natural person, the individual who signed it, using that person's own, individually provisioned credential. Nexiel Sign is the individual counterpart to Nexiel Seal, not a variant of the same check with a flag flipped: the two routes have separate authorization models, separate database tables, and separate error types, because they are separate real-world acts.

Both routes use the same underlying mechanism: a CSC (Cloud Signature Consortium) API v2.2 credential held at a QTSP (Qualified Trust Service Provider), never a shared Nexiel credential. Nexiel never stores the document itself, before or after signing, only SHA-256 hashes of both.

The visible signer name embedded in a signed PDF is KYB representative <signerId>, not a decrypted legal name. Nexiel Sign never decrypts the signer's stored personal data to populate this field. An integration that wants the signer's real name in the visible signature would need to supply it explicitly as part of its own flow (for example, from its own UI, where the signer types it themselves), never read silently out of Nexiel's records.

Before you start: the real gating chain

POST /v1/sign runs the following checks, in this order, before it signs anything:

  1. A real Keycloak-authenticated bearer token. There is no sandbox-mode accountholder for this route, and an unauthenticated request is always a 401.
  2. An active account: the same billing, relationship, and KYB-approval status check every authenticated Nexiel Screen route applies.
  3. A configured signer. No real QTSP account is provisioned in any deployment of this codebase today, so every real request stops here: 503 qes_not_configured, before signerId is evaluated at all.
  4. Per-client rate limiting, shared with Nexiel Screen's own request bucket.
  5. An optional Idempotency-Key header, enforced server-side, exactly like POST /v1/screen.
  6. signerId resolving to a real, declared UBO, director, or senior managing official on an APPROVED KYB application for the calling client. Only a real, on-file person may sign. No free-text name is accepted.
  7. That specific person having their own, individually provisioned QES credential (kyb_ubos.qes_credential_id). This is per-individual provisioning, unlike Nexiel Seal's one credential per client: each declared representative who signs needs their own credential provisioned before their first signature.

The last two checks, and the actual signature, only run once a real QTSP account exists for this deployment. Today, step 3 above stops every request first. The error responses for steps 6 and 7 below are real and independently tested against the full gating pipeline. You will not observe them from a live request against a deployment with no QTSP configured, because the request never reaches that far.

An optional pin and pinObjectIdpair is forwarded to the QTSP as the CSC API's authDataobject, to satisfy eIDAS Art. 26's “sole control” requirement for a remote qualified signature. Nexiel never validates this PIN itself. It is opaque, forwarded as-is, and never logged or persisted anywhere in this codebase.

Request and response shape

The real request and response schema for POST /v1/sign. Because this route always fails closed today (see production status below), the response shown is the documented schema, not a captured live response:

curl -s -X POST https://compliance.nexiel.io/v1/sign \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <access_token>" \
  -H "Idempotency-Key: 6c1a9e4f-2b6d-4830-9a1c-7e9b2d4f6a83" \
  -d '{
    "document": "JVBERi0xLjQKJcOkw7zDtsO...(base64-encoded PDF, truncated)",
    "signerId": "9d2b4f61-7a83-4c15-b6e2-8f04d3a91c67",
    "pin": "4471",
    "pinObjectId": "PIN"
  }'
200 (documented schema, once a QTSP account is configured)
{
  "operationId": "3f0a7c1e-9b6d-4a15-8305-c1e7b9d2f4a6",
  "signedDocument": "JVBERi0xLjQKJcOkw7zDtsO...(base64-encoded, truncated)",
  "documentHashSha256": "7c1a9e4f2b6d8305a1c7e9b2d4f6a8305c1e7b9d2f4a6c8305f1a9c7e2b4d6f8",
  "signedDocumentHashSha256": "4a7f1e9c2b6d8305f1a9c7e2b4d6f8305a1c7e9b2d4f6a8305c1e7b9d2f4a6c8",
  "frozenDecisionArtifact": {
    "cscCredentialId": "signer-credential-9d2b4f61",
    "qtspProvider": "d-trust",
    "engineVersion": "0.1.0-provisional",
    "cscApiSpecVersion": "2.2.0.0",
    "subFilter": "ETSI.CAdES.detached"
  }
}

document is base64-encoded PDF bytes, up to roughly an 11MB decoded ceiling. signerId is a kyb_ubos.id, never a free-text name. pin and pinObjectId are optional together, and omitted entirely for a non-interactive credential. frozenDecisionArtifact pins exactly which CSC credential, QTSP, engine version, and CSC API specification version produced the signature, the same regulator-facing discipline every screening decision in Nexiel Screen records.

Error responses

In the order the gating chain above can produce them:

401 (no valid bearer token)
{
  "error": "unauthorized",
  "message": "invalid or missing bearer token"
}
403 (account not active: billing, relationship, or KYB status)
{
  "error": "account_not_active",
  "message": "this account is not currently active"
}
503 (no QTSP account configured, the real state of every deployment today)
{
  "error": "qes_not_configured",
  "message": "Nexiel Sign is not configured for this deployment (QSEAL_CSC_BASE_URL / QSEAL_CSC_OAUTH2_TOKEN_URL / QSEAL_CSC_CLIENT_ID / QSEAL_CSC_CLIENT_SECRET are unset). no real QTSP account has been provisioned yet."
}
403 (no KYB-verified legal entity linked to this API key)
{
  "error": "no_linked_kyb_client",
  "message": "no clients.id is linked to this API key. Nexiel Sign requires a real, KYB-verified legal entity."
}
403 (signerId is not a declared representative on an APPROVED KYB application)
{
  "error": "signer_not_authorized",
  "message": "signerId does not resolve to a declared UBO/director/senior-managing-official on an APPROVED KYB application for this client. only this client's own declared representatives may sign."
}
409 (the named signer has no QES credential provisioned yet)
{
  "error": "signer_credential_not_provisioned",
  "message": "signer 9d2b4f61-7a83-4c15-b6e2-8f04d3a91c67 has no QES credential provisioned yet (kyb_ubos.qes_credential_id is null)."
}
409 (same Idempotency-Key reused with a different request body)
{
  "error": "idempotency_key_conflict",
  "message": "idempotency key already used with a different request"
}

What gets persisted

Production status

No real QTSP account exists yet for Nexiel Sign, or for Nexiel Seal. Every real deployment of this codebase today has qesSigner unset, so POST /v1/sign always returns 503 qes_not_configuredonce an authenticated, active-account caller reaches that check. This is a real, tested scaffold built against the published CSC API v2.2 specification: every gate described above, the CMS/PAdES signing pipeline underneath it, and the audit trail it writes are real code with real test coverage, including a cryptographic round-trip test (a real PDF, a real RSA keypair standing in for a QTSP's hardware security module, independent re-verification of the result via a separate code path). What is missing is a business relationship with a QTSP, not code. Provisioning one is a founder-level decision, not an engineering task.

Unlike most check types documented on this site, there is deliberately no sandbox-mode version of this route to try in the meantime. A QES is a real legal act with real consequences for the person signing, and a fake, unauthenticated version of it would risk a caller mistaking a synthetic signature for a real one.

Reference

There is no separate Nexiel Sign API reference page. POST /v1/sign is documented, with its full request and response schema, on the Screen API reference. See the Screen API quickstart's authentication sectionfor how to exchange a KYB-approved client's credentials for the bearer token this route requires.