API (source)¶
Engineering source file API.md.
OpenSignature REST API¶
Contract for Phase 6 signing, certificate, provider, and Phase 17 verification endpoints.
Base path: /api/v1
OpenAPI document (Development and Testing): GET /openapi/v1.json
Authentication (Authorization: ApiKey <key> or X-Api-Key) is enforced when Authentication:Enabled=true. Default is off for local smoke tests. Callers identify the tenant with X-Tenant-Id (must match the API key tenant when authenticated) or the key / configured default tenant.
Private keys are never returned by any endpoint. Certificate APIs expose public certificate metadata only.
Common headers¶
| Header | Required | Description |
|---|---|---|
Authorization / X-Api-Key |
Required when Authentication:Enabled=true |
MVP API key (Authorization: ApiKey <key> or X-Api-Key). Production: OAuth2/OIDC + JWT. |
X-Tenant-Id |
Recommended | Tenant scope for the request. If omitted, the API uses Signatures:DefaultTenantId. |
Idempotency-Key |
Recommended for POST /signatures |
Opaque client key. Replays with the same tenant + key return the original create result instead of a second job. |
X-Correlation-Id |
Optional | Client correlation id propagated into logs, audit, and status responses. |
Error model¶
Errors use RFC 7807 Problem Details. Machine-readable codes are in the errorCode extension (not code).
{
"type": "https://httpstatuses.com/400",
"title": "Invalid request",
"status": 400,
"detail": "A non-empty 'file' form field is required.",
"errorCode": "SIGNATURE_REQUEST_INVALID",
"traceId": "..."
}
Machine-readable error codes¶
| Code | Typical HTTP status | When |
|---|---|---|
SIGNATURE_REQUEST_INVALID |
400, 413 | Missing/invalid multipart, tenant, correlation id, empty upload, or size limit |
SIGNATURE_FORMAT_UNSUPPORTED |
400 | Unknown or unsupported format |
SIGNATURE_PROFILE_UNSUPPORTED |
400 | Unknown or unsupported profile |
SIGNING_PROVIDER_UNAVAILABLE |
400 | Unknown provider type or provider not available for the request |
SIGNING_CERTIFICATE_NOT_FOUND |
400 | Requested certificate thumbprint not found |
SIGNING_CERTIFICATE_EXPIRED |
422 / job failure | Certificate expired at signing time. Not raised when Signing:AllowExpiredCertificates is true. |
SIGNATURE_INPUT_NOT_FOUND |
404 | Signature id not found for the tenant |
SIGNATURE_OUTPUT_NOT_FOUND |
409 | Signed content requested before completion (or output missing) |
SIGNATURE_NOT_CANCELLABLE |
409 | Cancel requested after signing has progressed past a cancellable state |
SIGNATURE_ALREADY_COMPLETED |
409 | Operation conflicts with a completed signature |
SIGNING_OPERATION_FAILED |
500 / job failure | Unexpected signing or API failure |
SIGNING_PROVIDER_UNSUPPORTED |
job failure | Provider type not implemented for the job |
TIMESTAMP_AUTHORITY_UNAVAILABLE |
job failure | T/LT/LTA requested but no RFC 3161 TSA is configured |
TIMESTAMP_OPERATION_FAILED |
job failure | TSA HTTP/token validation failed (retried, then DLQ) |
SIGNATURE_VALIDATION_DATA_UNAVAILABLE |
job failure | LT/LTA requested but CRL/OCSP evidence is missing |
Clients should treat errorCode as stable for branching; detail is human-readable and may change.
Signatures¶
POST /api/v1/signatures¶
Creates an asynchronous signing job. The API validates input, stores the file, persists metadata, enqueues work via the outbox, and returns immediately. Cryptographic signing runs in the worker — never synchronously in the API.
Headers¶
Content-Type: multipart/form-data(required)X-Tenant-Id(recommended)Idempotency-Key(recommended)X-Correlation-Id(optional)Authorization(roadmap)
Multipart fields¶
| Field | Required | Values / notes |
|---|---|---|
file |
Yes | Document bytes. Non-empty. Subject to Signatures:MaxUploadBytes. |
format |
Yes | PAdES, XAdES, CAdES, ASiC_S, ASiC_E |
profile |
Yes | B, T, LT, LTA |
signingProvider |
Yes | Pfx, Pkcs11, SmartCard, Hsm |
certificateThumbprint |
No | Selects a certificate known to the provider (public metadata / thumbprint only) |
visibleSignature |
No | true / false. PAdES only. Draws a visible stamp on the selected page. A later PAdES request on an already-signed PDF adds a new signature instead of overwriting the previous one; visible stamps are placed so they do not overlap. |
signatureNote |
No | Optional text on the stamp and PDF /Reason. Max 500 characters. PAdES only. Implies visible when set. |
signaturePage |
No | 1-based page number for the stamp (default 1). PAdES only. |
signatureImage |
No | Optional JPEG or PNG stamp image (max Signatures:MaxAppearanceImageBytes, default 2 MiB). Stored separately from the document; never sent on RabbitMQ. PAdES only. Implies visible when set. |
Filenames and client MIME types are untrusted; storage keys are server-generated.
Responses¶
| Status | Meaning |
|---|---|
202 Accepted |
Job accepted. Body includes id, status (Queued), createdAt, statusUrl. Location may mirror statusUrl. |
400 Bad Request |
Invalid form, tenant, correlation id, format, profile, provider, or certificate |
413 Payload Too Large |
Upload exceeds configured max size (SIGNATURE_REQUEST_INVALID) |
Example 202 body:
{
"id": "0198...",
"status": "Queued",
"createdAt": "2026-09-05T17:00:00Z",
"statusUrl": "/api/v1/signatures/0198..."
}
GET /api/v1/signatures/{id}¶
Returns the current signing state for the tenant.
Headers¶
X-Tenant-Id(recommended)Authorization(roadmap)
Path¶
| Parameter | Type | Description |
|---|---|---|
id |
UUID | Signature request id |
Responses¶
| Status | Meaning |
|---|---|
200 OK |
Status payload |
400 Bad Request |
Invalid tenant (SIGNATURE_REQUEST_INVALID) |
404 Not Found |
Unknown id for tenant (SIGNATURE_INPUT_NOT_FOUND) |
Example 200 body:
{
"id": "0198...",
"tenantId": "tenant-demo",
"status": "Queued",
"format": "PAdES",
"profile": "B",
"signingProvider": "Pfx",
"visibleSignature": false,
"signatureNote": null,
"appearancePageNumber": 1,
"hasAppearanceImage": false,
"createdAt": "2026-09-05T17:00:00Z",
"queuedAt": "2026-09-05T17:00:00Z",
"startedAt": null,
"completedAt": null,
"failedAt": null,
"errorCode": null,
"errorMessage": null,
"correlationId": null,
"statusUrl": "/api/v1/signatures/0198..."
}
status values include domain states such as Queued, Processing, Completed, Failed, Cancelled, and related intermediate states.
GET /api/v1/signatures/{id}/content¶
Downloads the signed document when the request is Completed.
Headers¶
X-Tenant-Id(recommended)Authorization(roadmap)
Responses¶
| Status | Meaning |
|---|---|
200 OK |
Binary body (Content-Type / filename from stored output metadata) |
400 Bad Request |
Invalid tenant |
404 Not Found |
Unknown signature (SIGNATURE_INPUT_NOT_FOUND) |
409 Conflict |
Not ready or output missing (SIGNATURE_OUTPUT_NOT_FOUND) |
POST /api/v1/signatures/{id}/cancel¶
Requests cancellation. Reliable only before signing has progressed past a cancellable state (typically before / at early queue processing).
Headers¶
X-Tenant-Id(recommended)Authorization(roadmap)
Responses¶
| Status | Meaning |
|---|---|
200 OK |
Cancelled; body is the updated status payload |
400 Bad Request |
Invalid tenant |
404 Not Found |
Unknown signature (SIGNATURE_INPUT_NOT_FOUND) |
409 Conflict |
Not cancellable (SIGNATURE_NOT_CANCELLABLE) |
GET /api/v1/signatures/{id}/verification¶
Verifies the stored signed output of a completed signature request. Does not perform signing. Returns a detailed report: overall status (VALID / INVALID / INDETERMINATE), cryptographic check, certificate path, revocation, and machine-readable reason codes.
Coverage: CAdES (attached), XAdES, PAdES, and ASiC-S/E (inner CAdES). Cryptographic and certificate-path checks only — not a full ETSI EN 319 102-1 AdES conformance report (T/LT/LTA evidence is not independently evaluated).
Headers¶
X-Tenant-Id(recommended)Authorization(when authentication is enabled;SignaturesRead)
Responses¶
| Status | Meaning |
|---|---|
200 OK |
Detailed verification report |
400 Bad Request |
Invalid tenant |
404 Not Found |
Unknown signature (SIGNATURE_INPUT_NOT_FOUND) |
409 Conflict |
Not completed / output missing (SIGNATURE_OUTPUT_NOT_FOUND) |
Example 200 body:
{
"overallStatus": "VALID",
"isValid": true,
"reasonCodes": ["SIG_VALID", "CERT_VALID"],
"checkedAt": "2026-09-06T08:00:00Z",
"source": "StoredSignature",
"format": "CAdES",
"signatureId": "0198...",
"detail": null,
"limitations": "Cryptographic verification and certificate path checks for CAdES, XAdES, PAdES, and ASiC (ASiC unpacks the inner CAdES). Not a full ETSI EN 319 102-1 AdES conformance report: T/LT/LTA timestamps and revocation evidence are not independently evaluated.",
"signature": {
"cryptoValid": true,
"signerThumbprint": "...",
"signerSubject": "CN=...",
"reasonCodes": ["SIG_VALID"]
},
"certificate": {
"isValid": true,
"subject": "CN=...",
"issuer": "CN=...",
"thumbprint": "...",
"notBefore": "2026-01-01T00:00:00Z",
"notAfter": "2027-01-01T00:00:00Z",
"reasonCodes": ["CERT_VALID"],
"chainStatus": [],
"revocation": {
"status": "Unknown",
"source": "Offline",
"detail": null
}
}
}
Ad-hoc verification¶
POST /api/v1/verifications¶
Verifies an uploaded signed document in memory. The file is not persisted and is never placed on RabbitMQ.
Multipart fields¶
| Field | Required | Values / notes |
|---|---|---|
file |
Yes | Signed artifact (CMS, signed XML, or signed PDF). Subject to Signatures:MaxUploadBytes. |
format |
Yes | PAdES, XAdES, CAdES |
originalFile |
No | Original document for detached CAdES |
Responses¶
| Status | Meaning |
|---|---|
200 OK |
Detailed verification report (source: UploadedDocument) |
400 Bad Request |
Missing file/format (SIGNATURE_REQUEST_INVALID / SIGNATURE_FORMAT_UNSUPPORTED) |
413 Payload Too Large |
Upload exceeds configured max size |
Certificates¶
Public certificate metadata only. Endpoints never export private keys, PFX material, or hardware secrets.
GET /api/v1/certificates¶
Lists certificates visible to configured signing providers for the tenant/context.
Headers¶
X-Tenant-Id(recommended)Authorization(roadmap)
Responses (contract)¶
| Status | Meaning |
|---|---|
200 OK |
Array of public certificate descriptors (e.g. id, subject, issuer, notBefore, notAfter, thumbprint, provider id) |
401 / 403 |
When authentication/authorization is enabled |
Private key material is never included.
GET /api/v1/certificates/{id}¶
Returns one certificate’s public metadata.
Path¶
| Parameter | Description |
|---|---|
id |
Certificate identifier (implementation-defined: thumbprint or platform id) |
Responses (contract)¶
| Status | Meaning |
|---|---|
200 OK |
Public certificate metadata |
404 Not Found |
Unknown certificate |
401 / 403 |
When auth is enabled |
Does not return private keys or exportable key blobs.
Providers¶
GET /api/v1/providers¶
Lists configured signing providers (type, id, display name). Does not expose credentials, PIN, or keystore secrets.
USB tokens and smart cards appear as a SmartCard provider when Signing:SmartCard is configured (or auto-detected in Development). They are not enumerated as raw USB devices. See docs/OPERATIONS.md.
Headers¶
X-Tenant-Id(recommended)Authorization(roadmap)
Responses (contract)¶
| Status | Meaning |
|---|---|
200 OK |
Provider list |
401 / 403 |
When auth is enabled |
GET /api/v1/providers/{id}/health¶
Returns availability / health for a single provider (reachable, certificate store accessible, etc.). Diagnostics must not include secrets.
Path¶
| Parameter | Description |
|---|---|
id |
Provider id |
Responses (contract)¶
| Status | Meaning |
|---|---|
200 OK |
Health payload (status, optional detail, checked-at timestamp) |
404 Not Found |
Unknown provider |
503 Service Unavailable |
Provider unhealthy (optional; may also be 200 with degraded status) |
OpenAPI discovery¶
| Environment | Document |
|---|---|
| Development | GET /openapi/v1.json |
| Testing | GET /openapi/v1.json |
| Production | Not mapped by default |
The generated document includes signature routes under /api/v1/signatures (including /verification) and ad-hoc verification under /api/v1/verifications. Certificate and provider paths appear when those endpoint maps are registered.
Notes¶
- Document binaries are never placed in RabbitMQ messages; only job/metadata references are queued.
- Do not silently downgrade a requested signature profile. T/LT/LTA require worker
Timestamping:Url; LT/LTA also need CRL or OCSP evidence (TIMESTAMP_AUTHORITY_UNAVAILABLE/SIGNATURE_VALIDATION_DATA_UNAVAILABLE). - Verification reports cover CAdES/XAdES/PAdES/ASiC cryptographic and certificate-path checks; they are not full ETSI EN 319 102-1 AdES conformance reports.