PowerAuth Unit API
The PowerAuth Unit API registers a PowerAuth activation as a unit, creates its user-signing keys, revokes the unit and signs with the user-signing keys. Conventions, authentication, the encryption envelope and common types are described in API General Principles.
Target design; the endpoints are not implemented yet.
Error Handling
Generic error codes (validation, authentication, HSM, PowerAuth Server, internal) are listed once in Possible Error Codes. Each endpoint below shows its error responses as Response 4xx and Response 5xx tabs, with a sample problem body and the possible codes.
Unit Services
post /v1/pa/units Register a Unit
Registers the activation as a unit and creates its user-signing keys in one HSM session: always an ECDSA_P384 key, plus an MLDSA_65 or MLDSA_87 key (matching the device ML-DSA key) for a hybrid activation. The keys are sealed to the device keys held by PowerAuth Server. Call it once per activation.
- Caller: the Wallet Integration Server’s custom activation provider (
processCustomActivationAttributes), after the activation is prepared and before it is committed. - Authorization: a backend token only. There is no user authorization, because the activation is not committed yet, and no encryption.
- Response delivery: the response reaches the phone inside PowerAuth’s encrypted activation response (
customAttributes). PowerAuth Server neither receives nor stores it. - Checks: PowerAuth Server must report the activation as
PENDING_COMMITof the wallet application. A hybrid activation on an HSM without ML-DSA is refused with409 activation_state_invalidbefore any HSM call (request logreasonml_dsa_unavailable).
Request
Request Headers
Content-Type: application/json
Authorization: Bearer <token>
The backend caller token of the client.
Request Body
{
"unitId": "2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4"
}
Request Params
| Attribute | Type | Description |
|---|---|---|
unitId* |
UnitId |
The activation id. |
Response
The created user-signing keys.
{
"keys": [
{
"keyType": "ECDSA_P384",
"publicKey": "BJgePHg/44V+3i5vTcdUO/XGJgUS0rQ84DlOmzbNtV6NaUcSVw1KE5cuyRvO0V+HPAPV…",
"wrappedKey": "eyJ0eXAiOiJzay13cmFwcGVkLWtleStqd2UiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwidmVyIjoiMSIsImt0eSI6IkVDRFNBX1AzODQi…"
},
{
"keyType": "MLDSA_87",
"publicKey": "MIIKMjALBglghkgBZQMEAxMDggohA…",
"wrappedKey": "eyJ0eXAiOiJzay13cmFwcGVkLWtleStqd2UiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwidmVyIjoiMSIsImt0eSI6Ik1MRFNBXzg3Ii…"
}
]
}
Description of Response Params
| Attribute | Type | Description |
|---|---|---|
keys |
Object[] |
The user-signing keys, 1 or 2 items, ECDSA_P384 first (a classical activation gets only that one). |
keys[].keyType |
UserSigningKeyType |
Key type of the key. |
keys[].publicKey |
String |
Public key in PowerAuth’s serialization, standard Base64: the uncompressed point 0x04‖X‖Y for ECDSA_P384, X.509 SubjectPublicKeyInfo for ML-DSA. |
keys[].wrappedKey |
UserSigningWrappedKey |
The user-signing key (purpose USER_SIGNING, kty header = key type), sealed to the activation’s device keys. |
The request is invalid or refers to an unknown object.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_request"
}
Possible error codes:
invalid_request- The request fails a schema, pattern, encoding or transport rule (see Common Conventions).
The backend caller is not authenticated.
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"code": "backend_unauthenticated"
}
Possible error codes:
backend_unauthenticated- A missing, malformed or unknown bearer token. The response carriesWWW-Authenticate: Bearer.
The caller is not authorized for this request.
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"code": "backend_forbidden"
}
Possible error codes:
backend_forbidden- The backend client is not allowed to call the unit endpoints (nounits=true).
The request conflicts with the state of the unit, the key or the wrapped key.
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"code": "unit_already_exists"
}
Possible error codes:
unit_already_exists- The unit is already registered.unit_revoked- The unit is revoked (it cannot be registered again).activation_state_invalid- PowerAuth Server does not report the activationPENDING_COMMITfor the wallet application, it has no supported device-key set (ECDSA_P384, alone or withMLDSA_65orMLDSA_87), or the device-key policy refuses it.
An internal error occurred.
{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"code": "internal_error"
}
Possible error codes:
internal_error- A token or key configuration fault, or an unexpected error; no detail.
A dependency is unavailable; retry after the number of seconds in Retry-After.
{
"type": "about:blank",
"title": "Service Unavailable",
"status": 503,
"code": "hsm_unavailable"
}
Possible error codes:
hsm_unavailable- The HSM is unavailable.authorization_unavailable- PowerAuth Server is unreachable, timed out, or returned an error other than a refusal.
post /v1/pa/units/{unitId}/revocation Revoke a Unit
Revokes the unit permanently. Every later key operation and attestation for the unit gets 409 unit_revoked. The HSM is not called and nothing is deleted from it; the unit’s wrapped keys simply stop working.
The caller is the Wallet Integration Server, when the PowerAuth activation is removed or the user loses the device. The call uses a backend token, with no user authorization and no encryption.
Request
Request Headers
Content-Type: application/json
Authorization: Bearer <token>
The backend caller token of the client.
Path Params
| Param | Type | Description |
|---|---|---|
unitId* |
UnitId |
The unit id. |
Request Body
Exactly the empty object, so a body-less or cross-site request cannot revoke a unit.
{}
Response
No body.
The request is invalid or refers to an unknown object.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_request"
}
Possible error codes:
invalid_request- The request fails a schema, pattern, encoding or transport rule (see Common Conventions).unit_not_found- The unit does not exist.
The backend caller is not authenticated.
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"code": "backend_unauthenticated"
}
Possible error codes:
backend_unauthenticated- A missing, malformed or unknown bearer token. The response carriesWWW-Authenticate: Bearer.
The caller is not authorized for this request.
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"code": "backend_forbidden"
}
Possible error codes:
backend_forbidden- The backend client is not allowed to call the unit endpoints (nounits=true).
The request conflicts with the state of the unit, the key or the wrapped key.
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"code": "unit_revoked"
}
Possible error codes:
unit_revoked- The unit is already revoked (a repeated revocation, or a revocation during the call); a client treats it as done.
An internal error occurred.
{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"code": "internal_error"
}
Possible error codes:
internal_error- A token or key configuration fault, or an unexpected error; no detail.
post /v1/pa/units/{unitId}/signatures Sign with the User-Signing Keys
Signs one or two items with the unit’s user-signing keys, all or nothing. For ECDSA_P384, send the 48-byte SHA-384 digest of the data; for ML-DSA, send the data itself. The consuming service decides what the data is, for example a document.
The caller is the Wallet Integration Server, forwarding the wallet’s request unchanged. The request needs a vault unlock and device signatures: it is a signed request with aud /v1/pa/units/{unitId}/signatures, and the request and the success response are encrypted (see the envelope).
Request
Request Headers
Content-Type: application/json
X-PowerAuth-Encryption: PowerAuth version="4.0", application_key="znZ5XajbFZAq/jv2HCJLoQ==", activation_id="2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4"
The activation_id must equal the path unitId; see the envelope.
Path Params
| Param | Type | Description |
|---|---|---|
unitId* |
UnitId |
The unit id. |
Request Body
The sample is for a hybrid activation (P-384 and ML-DSA-87: both keys in one call), signed with ES384 and ML-DSA-87. The Claims tab shows the claims of the signed request (the members below are its request claim); the Envelope tab shows the request on the wire, the signed request encrypted in the envelope.
{
"aud": "/v1/pa/units/2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4/signatures",
"sub": "2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4",
"iat": 1793271600,
"jti": "Pm3sV8dQx1Lr6YtNa0KfWg",
"request": {
"items": [
{
"wrappedKey": "eyJ0eXAiOiJzay13cmFwcGVkLWtleStqd2UiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwidmVyIjoiMSIsImt0eSI6IkVDRFNBX1AzODQi…",
"signingInput": "EvkbqQuMzP35x2OBXfN5EPCh-1vfDX69J5rWbacYO1syVkRBU3HW1qZUsDlMYwbh"
},
{
"wrappedKey": "eyJ0eXAiOiJzay13cmFwcGVkLWtleStqd2UiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwidmVyIjoiMSIsImt0eSI6Ik1MRFNBXzg3Ii…",
"signingInput": "dXNlci1zaWduaW5nIGRhdGEgZXhhbXBsZQ…"
}
]
}
}
POST /v1/pa/units/2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4/signatures HTTP/1.1
Content-Type: application/json
X-PowerAuth-Encryption: PowerAuth version="4.0", application_key="znZ5XajbFZAq/jv2HCJLoQ==", activation_id="2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4"
{"temporaryKeyId":"e8cd1538-ef37-4c8e-b745-0e8e9f326ecc","encryptedData":"GbydIAxxoU8qUFHUhpZQa1BzAJyK2/myJsLJtpJWjz8…","nonce":"CVBa2DGm1w1gQvM0h6WUXHG8S+cm0tCd","timestamp":1793271600123}
Request Params
| Attribute | Type | Description |
|---|---|---|
items* |
Object[] |
1 or 2 items of distinct key types, all signed or none. |
items[].wrappedKey* |
UserSigningWrappedKey |
One of the unit’s user-signing keys; its authenticated kty fixes the algorithm. |
items[].signingInput* |
String |
Base64url without padding (canonical), at most 21,846 characters. ECDSA_P384: the SHA-384 digest, exactly 48 bytes (another length is 400, detail items[<i>].signingInput: length). ML-DSA: the data, 1 to 16,384 bytes. |
The device signatures cover the whole request; the stored key type fixes the algorithm, never the request. The items’ key types must be distinct user-signing key types of the unit (ECDSA_P384, plus the ML-DSA type of its device ML-DSA key), otherwise 409 wrapped_key_invalid.
Response
The decrypted response; on the wire the body is an EncryptedResponse (see the envelope).
{
"signatures": [
{"keyType": "ECDSA_P384", "signature": "5lAaPruIx2tXxg8ijES531l4sqVlL7VxKObLAAZ4s2O5…"},
{"keyType": "MLDSA_87", "signature": "x3Jq0nZ8bVd1rKpT…"}
]
}
Description of Response Params
| Attribute | Type | Description |
|---|---|---|
signatures |
Object[] |
One signature per item, in item order. |
signatures[].keyType |
UserSigningKeyType |
Key type of the user-signing key, from its authenticated header. |
signatures[].signature |
String |
Base64url, 128 to 6170 characters. Raw R‖S for ECDSA_P384 (convert to DER where a format needs it), the FIPS 204 signature for ML-DSA. |
The request is invalid or refers to an unknown object.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_request"
}
Possible error codes:
invalid_request- The request fails a schema, pattern, encoding or transport rule (see Common Conventions).unit_not_found- The unit does not exist.
The caller is not authorized for this request.
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"code": "invalid_user_authorization"
}
Possible error codes:
invalid_user_authorization- The encryption header, the signed request or the activation status refuses the request.
The request conflicts with the state of the unit, the key or the wrapped key.
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"code": "unit_revoked"
}
Possible error codes:
unit_revoked- The unit is revoked.wrapped_key_invalid- A wrapped key is tampered, of another unit, purpose, key type, format version or generation, or malformed inside; in a later version also too old (maximum age). One byte-identical body for every cause.
An internal error occurred.
{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"code": "internal_error"
}
Possible error codes:
internal_error- A token or key configuration fault, or an unexpected error; no detail.
A dependency is unavailable; retry after the number of seconds in Retry-After.
{
"type": "about:blank",
"title": "Service Unavailable",
"status": 503,
"code": "hsm_unavailable"
}
Possible error codes:
hsm_unavailable- The HSM is unavailable.authorization_unavailable- PowerAuth Server is unreachable, timed out, or returned an error other than a refusal.