Signature API
The Signature API provides post-quantum digital signatures using the Dilithium3 algorithm (NIST ML-DSA-65, FIPS 204). Use it to sign messages and verify signatures with quantum-resistant security.
Signing operations happen inside the Qpher secure enclave. Your private signing keys are never exposed or exported.
Pass "algorithm": "Composite-ML-DSA" in sign/verify requests to use hybrid signatures
(ECDSA P-256 + ML-DSA-65). If omitted, the default is "Dilithium3" (PQC-only, backward-compatible).
Both ECDSA and ML-DSA components must verify for the signature to be valid, ensuring security if either algorithm is compromised.
Important: Composite ML-DSA signatures use Qpher's internal wire format and must be verified using this API. They are not interoperable with external Composite ML-DSA implementations.
See Security Architecture for details.
Signâ
Signs the provided message with the specified key version. The key must be in active status.
Request bodyâ
| Field | Type | Required | Description |
|---|---|---|---|
message | string (base64) | Yes | Base64-encoded message to sign. |
key_version | integer | Yes | The version of the Dilithium3 key to use. Must reference an active key. |
algorithm | string | No | "Dilithium3" (default) or "Composite-ML-DSA" (hybrid, Starter+). If omitted, defaults to Dilithium3. |
Response (200 OK)â
| Field | Type | Description |
|---|---|---|
data.signature | string (base64) | The signature, base64-encoded. |
data.key_version | integer | The key version used for signing. |
data.algorithm | string | "Dilithium3" or "Composite-ML-DSA", matching the algorithm used for the operation. |
Exampleâ
/api/v1/signature/signX-API-Key: qph_your_key_here
Content-Type: application/json{
"message": "SW52b2ljZSAjMTIzNCAtIFRvdGFsOiAkOTkuMDA=",
"key_version": 1
}{
"data": {
"signature": "c2lnbmF0dXJlX2J5dGVzX2hlcmUuLi4=",
"key_version": 1,
"algorithm": "Dilithium3"
},
"request_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"timestamp": "2026-01-15T10:30:02.000Z"
}curl -X POST https://api.qpher.ai/api/v1/signature/sign \
-H "X-API-Key: qph_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"message": "SW52b2ljZSAjMTIzNCAtIFRvdGFsOiAkOTkuMDA=",
"key_version": 1
}'Errorsâ
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | ERR_SIG_001 | Invalid sign request â missing message, bad base64 encoding, or invalid key_version. |
| 400 | ERR_SIG_002 | Message exceeds maximum allowed size. |
| 401 | ERR_AUTH_001 | Missing or invalid API key. |
| 404 | ERR_NOT_FOUND_001 | Key version not found or key is not in active status. |
| 429 | ERR_RATE_LIMIT_001 | Rate limit exceeded â reduce request frequency or upgrade your plan. |
Verifyâ
Verifies that a signature is valid for the given message and key version. Verification can use keys in active or retired status.
Request bodyâ
| Field | Type | Required | Description |
|---|---|---|---|
message | string (base64) | Yes | Base64-encoded original message that was signed. |
signature | string (base64) | Yes | Base64-encoded signature to verify. |
key_version | integer | Yes | The key version that was used to create the signature. |
algorithm | string | No | "Dilithium3" (default) or "Composite-ML-DSA" (hybrid, Starter+). If omitted, defaults to Dilithium3. |
Response (200 OK)â
| Field | Type | Description |
|---|---|---|
data.valid | boolean | true if the signature is valid for the given message and key, false otherwise. |
data.key_version | integer | The key version used for verification. |
data.algorithm | string | "Dilithium3" or "Composite-ML-DSA", matching the algorithm used for the operation. |
Exampleâ
/api/v1/signature/verifyX-API-Key: qph_your_key_here
Content-Type: application/json{
"message": "SW52b2ljZSAjMTIzNCAtIFRvdGFsOiAkOTkuMDA=",
"signature": "c2lnbmF0dXJlX2J5dGVzX2hlcmUuLi4=",
"key_version": 1
}{
"data": {
"valid": true,
"key_version": 1,
"algorithm": "Dilithium3"
},
"request_id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
"timestamp": "2026-01-15T10:30:03.000Z"
}curl -X POST https://api.qpher.ai/api/v1/signature/verify \
-H "X-API-Key: qph_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"message": "SW52b2ljZSAjMTIzNCAtIFRvdGFsOiAkOTkuMDA=",
"signature": "c2lnbmF0dXJlX2J5dGVzX2hlcmUuLi4=",
"key_version": 1
}'Errorsâ
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | ERR_SIG_001 | Invalid verify request â bad base64 encoding for message or signature. |
| 400 | ERR_SIG_002 | Message or signature exceeds maximum allowed size. |
| 401 | ERR_AUTH_001 | Missing or invalid API key. |
| 404 | ERR_NOT_FOUND_001 | Key version not found or key is in archived status. |
| 429 | ERR_RATE_LIMIT_001 | Rate limit exceeded â reduce request frequency or upgrade your plan. |
A signature that does not match returns {"valid": false} with HTTP 200 â not an error. Errors (4xx/5xx) indicate malformed requests or missing resources, not invalid signatures.
Sign Hashâ
Signs a pre-computed hash digest instead of the raw message. This is ideal for large files or artifacts where you compute the hash client-side and send only the digest. The key must be in active status.
Request bodyâ
| Field | Type | Required | Description |
|---|---|---|---|
hash | string (base64) | Yes | Base64-encoded hash digest to sign. |
hash_algorithm | string | Yes | Hash algorithm used to compute the digest: "SHA-256", "SHA-384", or "SHA-512". |
key_version | integer | Yes | The version of the Dilithium3 key to use. Must reference an active key. |
algorithm | string | No | "Dilithium3" (default) or "Composite-ML-DSA" (hybrid, Starter+). If omitted, defaults to Dilithium3. |
Response (200 OK)â
| Field | Type | Description |
|---|---|---|
data.signature | string (base64) | The signature, base64-encoded. |
data.key_version | integer | The key version used for signing. |
data.algorithm | string | "Dilithium3" or "Composite-ML-DSA", matching the algorithm used for the operation. |
data.hash_algorithm | string | The hash algorithm that was specified in the request. |
data.signature_type | string | Always "detached" â indicates the signature covers a hash, not the raw message. |
Exampleâ
curl -X POST https://api.qpher.ai/api/v1/signature/sign-hash \
-H "X-API-Key: qph_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"hash": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
"hash_algorithm": "SHA-256",
"key_version": 1
}'Errorsâ
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | ERR_SIG_010 | Unsupported hash algorithm â use SHA-256, SHA-384, or SHA-512. |
| 400 | ERR_SIG_011 | Hash length does not match declared hash_algorithm (SHA-256=32, SHA-384=48, SHA-512=64 bytes). |
| 401 | ERR_AUTH_001 | Missing or invalid API key. |
| 404 | ERR_NOT_FOUND_001 | Key version not found or key is not in active status. |
| 429 | ERR_RATE_LIMIT_001 | Rate limit exceeded â reduce request frequency or upgrade your plan. |
Verify Hashâ
Verifies that a signature is valid for the given hash digest and key version. Verification can use keys in active or retired status.
Request bodyâ
| Field | Type | Required | Description |
|---|---|---|---|
hash | string (base64) | Yes | Base64-encoded hash digest that was signed. |
hash_algorithm | string | Yes | Hash algorithm used to compute the digest: "SHA-256", "SHA-384", or "SHA-512". |
signature | string (base64) | Yes | Base64-encoded signature to verify. |
key_version | integer | Yes | The key version that was used to create the signature. |
algorithm | string | No | "Dilithium3" (default) or "Composite-ML-DSA" (hybrid, Starter+). If omitted, defaults to Dilithium3. |
Response (200 OK)â
| Field | Type | Description |
|---|---|---|
data.valid | boolean | true if the signature is valid for the given hash and key, false otherwise. |
data.key_version | integer | The key version used for verification. |
data.algorithm | string | "Dilithium3" or "Composite-ML-DSA", matching the algorithm used for the operation. |
data.hash_algorithm | string | The hash algorithm that was specified in the request. |
Exampleâ
curl -X POST https://api.qpher.ai/api/v1/signature/verify-hash \
-H "X-API-Key: qph_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"hash": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
"hash_algorithm": "SHA-256",
"signature": "c2lnbmF0dXJlX2J5dGVzX2hlcmUuLi4=",
"key_version": 1
}'Errorsâ
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | ERR_SIG_010 | Unsupported hash algorithm â use SHA-256, SHA-384, or SHA-512. |
| 400 | ERR_SIG_011 | Hash length does not match declared hash_algorithm (SHA-256=32, SHA-384=48, SHA-512=64 bytes). |
| 401 | ERR_AUTH_001 | Missing or invalid API key. |
| 404 | ERR_NOT_FOUND_001 | Key version not found or key is in archived status. |
| 429 | ERR_RATE_LIMIT_001 | Rate limit exceeded â reduce request frequency or upgrade your plan. |
A signature that does not match returns {"valid": false} with HTTP 200 â not an error. Errors (4xx/5xx) indicate malformed requests or missing resources, not invalid signatures.
Key Version Requirementsâ
The key_version field is mandatory on all signature operations. There is no implicit "use latest key" behavior.
| Operation | Allowed Key Statuses |
|---|---|
| Sign | active only |
| Verify | active or retired |
| Sign Hash | active only |
| Verify Hash | active or retired |
Use the Key Management API to list your keys and find the current active version.