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_COMMIT of the wallet application. A hybrid activation on an HSM without ML-DSA is refused with 409 activation_state_invalid before any HSM call (request log reason ml_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 carries WWW-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 (no units=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 activation PENDING_COMMIT for the wallet application, it has no supported device-key set (ECDSA_P384, alone or with MLDSA_65 or MLDSA_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 carries WWW-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 (no units=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.
Last updated on Nov 11, 2026 (21:37) Edit on Github Send Feedback

develop

Secure Keystore