Skip to main content
Some API calls (for example, v3/api/servicedesk/approve) require an additional signing process with the maker/authoriser private key, typically when the maker confirms/authoriser approves an instruction (see section “4. Confirm instruction as maker” below). Before submitting the instruction to the HSM, you will need to sign the request payload with api_user_pri_key (API user private key). The content that must be signed is the stringified value of a part of this payload. For more details about a stringified value, see json-stable-stringify
The signed payload must not include optional and empty fields
The content to sign is the stringified value of the object named request and the resulting signature string should substitute placeholder $$REPLACE. Assume the maker is confirming instructions, retreive the instructions using the API v3/api/servicedesk/pending, the response is like (no field ordering)
{
  "request": {
    "key2": "value2",
    "key1": "value1",   
    "key3": "value3"
  },
  "signature": "$$REPLACE$$"
}

Here the request payload needs to be ordered like,
{
  "key1": "value1",   
  "key2": "value2",
  "key3": "value3"
}

This is the string to be used for generating the signature using api_user_pri_key, use it as value for signature field in the request. Below is an example of how to sign it in Python:
import os, base64, json
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec

def stringify_transaction_details():
    transaction_details = {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  }

    return json.dumps(transaction_details, sort_keys=True, separators=(',', ':'))

def sign_transaction_details(details, user):
    with open(os.path.join("keys", user + ".private.pem"), "rb") as private_key_in:
        ec_private_key = serialization.load_pem_private_key(private_key_in.read(),
                                                            password=None,
                                                            backend=default_backend())
        signed_payload = ec_private_key.sign(str.encode(details), ec.ECDSA(hashes.SHA256()))
    b64_signed_payload = base64.b64encode(signed_payload).decode('UTF-8')
    return b64_signed_payload

def create_transaction_payload(details, signature):
    payload = {
            "request": json.loads(details),
            "signature": signature  # this is signature of the json i.e. transaction_details
        }
    }
    return json.dumps(payload, sort_keys=True, separators=(',', ':'))

if __name__ == "__main__":
    stringified_details = stringify_transaction_details()
    user_signature = sign_transaction_details(stringified_details, "api-maker@zodia.io")
    transaction_payload = create_transaction_payload(stringified_details, user_signature)

    print(transaction_payload)
    

You will obtain the payload below (prettified for the documentation) to be submitted to the API:
{
  "request": {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  },
  "signature": "MEQCIHuLPOFmbnjWnJ0l6QpYRLMh/6LTwIV6LNlGXRiChProAiBjDQCsvbsGxb6pw9Il/m8zIFmZ+Wh3QFwBmIZJCOk8kA=="
}

Note: Please make sure you submit the same ordered request payload in the request body as well.