If you encounter Invalid digest error (error.digest.invalid), please consider the following:
- Check our documentation regarding Digest calculation:
Example code: eIDAS signature creation (Access Authorization API)
- Download Postman collections and test your request:
Swagger and Postman files (Nordea API Market Resources)
- The whole request body should be present in the digest. Refer to:
Redirect - Access Authorisation API
Authentication with eIDAS certificate
- Digest should be calculated based on a request body in JSON format (as it is sent to Nordea in the request). Converting the request body into the string is incorrect
- When making requests to Nordea Open Banking APIs, please ensure that SHA-256 algorithm is used
- To correctly calculate a digest it's necessary to operate on bytes
- The payload-rich requests require a Digest header to be present and mentioned in the Signature header message elements. Refer to:
Authentication with eIDAS certificate
Then the Signature header should look as follows:
'Signature': 'keyId="<client_id>",algorithm="rsa-sha256",headers="(request-target) x-nordea-originating-host x-nordea-originating-date content-type digest",signature="<encoded_signature_message>"'
- Digest should be calculated on the same values as in the request payload and the scope value should be a list (not a string). Refer to:
Redirect - Access Authorisation API
- (Sandbox) When a signature is used as 'SKIP_SIGNATURE_VALIDATION_FOR_SANDBOX' you can remove digest header from the request and still the request will go through by skipping the signature related validation
- Check if the payload format is a valid JSON
- (Corporate Access Authorization) Digest and Signature headers values are not hardcoded. These values are generated according to:
How do I sign my API requests? (Corporate Access Authorization API)
Postman collection includes the logic for generating these values as a pre-request script:
Creating signature header with Postman (Corporate Access Authorization API)
- (Corporate Access Authorization) When content type is application/json, like for:
endpoint, then the string used to calculate digest should be the same json as sent in the request, for example:
{
"scope": ["PAYMENTS_BROADBAND"],
"duration": 2628000,
"agreement_number":"130{redacted}200"
}
Digest header: sha-256=PhK9Uc7CH20I2FyoPR8m9IWlg520MvtSU2FbuNHFvso=
Check Content-Length of request body for unwanted spaces/tabs (e.g. one additional space will generate a different digest)
- Remember to use a proper endpoint for a token exchange. Refer to:
API reference - Access Authorization
Business Access Authorization API
API Reference - Business Access Authorization
API Reference - Corporate Access Authorization API
- When creating a digest it is important to have an alphabetical order in the Payload:
How can I get started signing with eIDAS?
Note: alphabetical order is valid when content type is application/x-www-form-urlencoded. For example for:
POST /corporate/v2/authorize/token
endpoint, string used to calculate digest should look like:
code=eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGl......5naBz1bVRyCuGdzHax6tJA&grant_type=authorization_code- If you use Python requests, make sure arrays are formatted correctly in the request body and the scope is passed as a [string]
-
Make sure you don't URL-encode parameters used to calculate a digest
FAQ
Q:
Do you have any C# code available for creating a digest header?
A:
We do not have an example in C#, but to help you verify your code please compare your results with the example we created by hashing the word "test":
n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=It was created using SHA-256 algorithm. As long as you provide the same input ('test') to your digest function, it should return exactly the same output as in this example.
Q:
Does the digest header need to be SHA-256 hashed and then Base64 encoded? We used SHA-256 (for example on: https://emn178.github.io/online-tools/sha256.html) as a comparison to our own code and with the word “test” it returns:
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08which is Base64 encoded into:
OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==A:
The hash before encoding as Base64:
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08is correct but these are bytes in hexadecimal format and you have incorrectly treated it as a text and Base64 encoded it.
As an example, please consider this page: https://base64.guru/converter/encode/hex. It understands that you're encoding bytes in hex format and provides a valid Base64 encoded hash:
n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=If you try this one instead: https://base64.guru/converter/encode/text you will see that it produces:
OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA== which matches your result.
To summarize: beginning of the hash '9f' (or '9F' as it's usually displayed) is a byte representing number 237. But text '9f' has different meaning (and '9F' yet another one). To correctly calculate digest it's necessary to operate on bytes (that complexity is usually hidden behind a library or a tool you are using).
Q:
While calling:
POST personal/v5/authorize/token
we encounter the following error: "code":"error.digest.invalid","description":"Invalid digest." What could be the reason?
A:
Make sure that:
- parameters used to calculate digest are not URL-encoded, e.g.:
code=ZXlKbGJtTWlPa{redacted}dXRGZEctaWdsdw==&grant_type=authorization_code&redirect_uri=https://example.example.io/callback
- response body you send is URL-encoded, e.g.:
code=ZXlKbGJtTWlPa{redacted}dXRGZEctaWdsdw%3D%3D&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fexample.example.io%2Fcallback
Q:
(Sandbox) We experience digest error - "code":"error.digest.invalid","description":"Invalid digest." - with the token retrieval endpoint. The corporate authorization flow works correctly until this step:
Error: {"code":"error.digest.invalid","description":"Invalid digest."}Endpoint: POST /corporate/v2/authorize/tokenStatus Code: 401 Unauthorized
Request Configuration:
Client ID: 3e509***b4bContent-Type: application/x-www-form-urlencodedCertificate: QSealC-NordeaDevPortal-2021.p12 (loaded successfully)
Current Digest Implementation:
Request Body: grant_type=authorization_code&code=eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0***Body Length: 707 bytesDigest Calculation Method: SHA-256 hash of raw request body → base64 encodeCalculated Digest: SHA-256=sYe0runU69***1Dq/jl8=Digest Header: digest: SHA-256=sYe0runU69***1Dq/jl8=
Complete Headers Sent:
Content-Type: application/x-www-form-urlencodedx-ibm-client-id: <clientId>x-ibm-client-secret: <secret>x-nordea-originating-date: Fri, 18 Jul 2025 09:06:46 GMTx-nordea-originating-host: api.nordeaopenbanking.comaccept: application/jsondigest: SHA-256=sYe0runU69***q/jl8=signature: keyId="<clientId>",algorithm="rsa-sha256",headers="(request-target) x-nordea-originating-host x-nordea-originating-date content-type digest",signature="..."
Signature String Used (includes digest):
(request-target): post /corporate/v2/authorize/tokenx-nordea-originating-host: api.nordeaopenbanking.comx-nordea-originating-date: Fri, 18 Jul 2025 09:06:46 GMTcontent-type: application/x-www-form-urlencodeddigest: SHA-256=sYe0runU69***Dq/jl8=
We've successfully implemented the signing mechanism but we fail on the digest validation. What could be the reason?
A:
Please make sure the body of the request is in alphabetical order.
Q:
While requesting:
POST personal/v5/decoupled/authorizations
in Sandbox, we receive a URL-encoded authorization code in response:
HTTP/1.1 200 OKDate: Tue, 12 Aug 2025 12:56:43 GMTContent-Type: application/jsonContent-Length: 1175Connection: keep-aliveVary: Origin,Access-Control-Request-Method,Access-Control-Request-HeadersCache-Control: no-cache, no-store, max-age=0, must-revalidatePragma: no-cacheExpires: 0X-Content-Type-Options: nosniffStrict-Transport-Security: max-age=31536000; includeSubDomainsX-Frame-Options: DENYX-XSS-Protection: 0Referrer-Policy: no-referrerX-Global-Transaction-ID: dd51***aea3X-RateLimit-Limit: name=default-rate,10000;X-RateLimit-Remaining: name=default-rate,9987;X-Proxygen-Request-Success: trueX-Postigen-Response-URL: https://api.nordeaopenbanking.com/personal/v5/decoupled/authorizations
{"code": "ZXlKbGJtT***UlKS0JmZw%3D%3D"}
Should we expect the same URL-encoded response for this endpoint in Production?
A:
Yes, you should expect a URL-encoded response in Production as well.