Skip to content

Generate HMAC headers - Python

The document API uses HMAC for authentication. You must use your private key and public key to create an HMAC digest for a request and pass that as part of the Authorization header.

The python code below shows the function that can be used to generate the headers for such a request:

py
def generate_headers(method, path):
    """Generates authentication headers for the request."""
    private_key = os.environ.get('UNOPDF_PRIVATE_KEY')
    public_key = os.environ.get('UNOPDF_PUBLIC_KEY')
    timestamp = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
    payload = method.upper() +"\n" + timestamp + "\n" + path.lower()

    digest = hmac.new(private_key.encode('utf-8'), msg=payload.encode('utf-8'), digestmod=hashlib.sha256).digest()
    base64_encoded = base64.b64encode(digest)
    url_safe = base64_encoded.rstrip(b'=').replace(b'+', b'-').replace(b'/', b'_').decode('utf-8')
    auth_header = "SharedKey: " + public_key + ":" + url_safe

    return {'Authorization': auth_header, 'Timestamp': timestamp, 'Accept': 'application/json'}

See the Authentication guide for more information.

The example below shows a fully working example that calls the /v1/files endpoint using HMAC signing.

Code

py
import base64
import datetime
import hashlib
import hmac
import os
import requests
from dotenv import load_dotenv

load_dotenv() 

def get_error(response):
    """Extracts error message from a response object."""
    try:
        response_body = response.json()
    except ValueError:
        response_body = response.text.strip()

    if isinstance(response_body, dict) and 'errors' in response_body:
        errors = response_body['errors']
        if isinstance(errors, dict):
            return ','.join(str(v) for v in errors.values())
        return str(errors)

    return str(response_body) or response.reason

#region generate_headers
def generate_headers(method, path):
    """Generates authentication headers for the request."""
    private_key = os.environ.get('UNOPDF_PRIVATE_KEY')
    public_key = os.environ.get('UNOPDF_PUBLIC_KEY')
    timestamp = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
    payload = method.upper() +"\n" + timestamp + "\n" + path.lower()

    digest = hmac.new(private_key.encode('utf-8'), msg=payload.encode('utf-8'), digestmod=hashlib.sha256).digest()
    base64_encoded = base64.b64encode(digest)
    url_safe = base64_encoded.rstrip(b'=').replace(b'+', b'-').replace(b'/', b'_').decode('utf-8')
    auth_header = "SharedKey: " + public_key + ":" + url_safe

    return {'Authorization': auth_header, 'Timestamp': timestamp, 'Accept': 'application/json'}
#endregion generate_headers

def main():
    """Main function to execute the script."""
    path = "/v1/files"
    url = os.environ.get('UNOPDF_API_URL') + path
    method = 'GET'

    headers = generate_headers(method, path)
    response = requests.request(method, url, headers=headers, timeout=30)

    if response.status_code != 200:
        message = get_error(response)
        raise Exception("Error \"" +  message + "\" calling UnoPdf API \"GET " + url + "\".")

    print("ok")

if __name__ == "__main__":
    main()
txt
requests
python-dotenv