This example uses a configuration where the entire payloads of both the request and the response are encrypted.
encryption.py
import os
import base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import requests
import json

api_endpoint = "https://live.deck.co/api/v1/jobs/submit"

# Your API keys
client_id = "xxx"
secret = "xxx"

# Your encryption key
encryption_key = "xxx"

# Prepare your payload
payload = {
  "job_code": "EnsureConnection",
  "input": {
    "source_guid": "xxx",
    "username": "xxx",
    "password": "xxx"
  }
}

class AESEncryptionHelper:
    KEY_SIZE = 32  # 256 bits / 8
    NONCE_SIZE = 12  # 96 bits / 8
    TAG_SIZE = 16  # 128 bits / 8

    @staticmethod
    def generate_key():
        """
        Generates a new AES-256 key suitable for use with AES-GCM.
        
        Note: This is for testing and demonstration purposes only.
        Valid encryption keys for production use will be provided by Deck.
        """
        key = os.urandom(AESEncryptionHelper.KEY_SIZE)
        return base64.b64encode(key).decode('utf-8')

    @staticmethod
    def encrypt(plaintext, base64_key):
        """Encrypts a plaintext string using AES-256-GCM."""
        if not plaintext:
            raise ValueError("Plaintext cannot be empty")
        if not base64_key:
            raise ValueError("Key cannot be empty")

        key = base64.b64decode(base64_key)
        if len(key) != AESEncryptionHelper.KEY_SIZE:
            raise ValueError(f"Key must be {AESEncryptionHelper.KEY_SIZE} bytes")

        plaintext_bytes = plaintext.encode('utf-8')
        
        # Generate nonce
        nonce = os.urandom(AESEncryptionHelper.NONCE_SIZE)
        
        # Encrypt
        aesgcm = AESGCM(key)
        ciphertext = aesgcm.encrypt(nonce, plaintext_bytes, None)
        
        # Extract tag (last 16 bytes) and actual ciphertext
        actual_ciphertext = ciphertext[:-AESEncryptionHelper.TAG_SIZE]
        tag = ciphertext[-AESEncryptionHelper.TAG_SIZE:]
        
        # Combine: nonce + ciphertext + tag
        result = nonce + actual_ciphertext + tag
        
        return base64.b64encode(result).decode('utf-8')

    @staticmethod
    def decrypt(encrypted_data, base64_key):
        """Decrypts data that was encrypted using AES-256-GCM."""
        if not encrypted_data:
            raise ValueError("Encrypted data cannot be empty")
        if not base64_key:
            raise ValueError("Key cannot be empty")

        key = base64.b64decode(base64_key)
        if len(key) != AESEncryptionHelper.KEY_SIZE:
            raise ValueError(f"Key must be {AESEncryptionHelper.KEY_SIZE} bytes")

        data = base64.b64decode(encrypted_data)
        if len(data) < AESEncryptionHelper.NONCE_SIZE + AESEncryptionHelper.TAG_SIZE:
            raise ValueError("Invalid encrypted data format")

        # Extract components
        nonce = data[:AESEncryptionHelper.NONCE_SIZE]
        ciphertext = data[AESEncryptionHelper.NONCE_SIZE:-AESEncryptionHelper.TAG_SIZE]
        tag = data[-AESEncryptionHelper.TAG_SIZE:]
        
        # Decrypt
        aesgcm = AESGCM(key)
        plaintext = aesgcm.decrypt(nonce, ciphertext + tag, None)
        
        return plaintext.decode('utf-8')

# Initialize encryption helper
helper = AESEncryptionHelper()

# Convert to JSON and encrypt
json_payload = json.dumps(payload)
encrypted_payload = helper.encrypt(json_payload, encryption_key)

# Send to API using standardized format

payload = {
    "encrypted_payload": encrypted_payload,
    "encryption_version": "1.0"
}
print("Request Body:")
print(json.dumps(payload, indent=4))

response = requests.post(
    api_endpoint,
    headers={
        "Content-Type": "application/json+encrypted",
        "x-deck-client-id": client_id,
        "x-deck-secret": secret
    },
    json=payload
)

# Decrypt the response
if response.status_code == 200 or response.status_code == 202:
    response_data = response.json()
    print("\r\nResponse Body:")
    print(response_data)
    encrypted_response = response_data["encrypted_payload"]
    
    decrypted_response = helper.decrypt(encrypted_response, encryption_key)
    result = json.loads(decrypted_response)
    print("\r\nDecrypted response Payload:")
    print(result)
else:
    print(response.status_code)

Output

Upon successful execution using your API Keys, the program will produce an output similar to this:
Request Body:
{
    "encrypted_payload": "b3Vcxxgp1/Vj74duDr3oQEmaAEqkcKDDLNiO8Pj55voKp4/X+d8sSZImd8awWTP/5+HUslkMelqvl2WcjPtbv5qpHfIPbt3IxIZoH+S8ictU0LOtDhRBXP4mJM8CptcH7roDx2G8F28wyM1FCY/ZUO5TLJfsk5XHY7vzq/quqU2SNMQrSCNd8y7IHtyX/65BsaEuZixdXdzWa5KfW7Cr+GAVXONEo6+vgXNTZlZ8NnOv+lLBbCuyaiEaPsGAm9NARJHxfKc=",
    "encryption_version": "1.0"
}

Response Body:
{'encrypted_payload': 'rpcnQBAb0k+6ctcXyRgfk06WhCyHTxv+xNMKjCIdiue9U5b97AfM2a+OIJRkpQ2kPqh4WIOQ81nL3wczRUwLa6+9/2gSIqNb42Pr+osmuKkdibkC49ip1GGz2HNH4dq76G4vsPx3Hs728Sc3QQ==', 'encryption_version': '1.0'}

Decrypted response Payload:
{'job_guid': 'acc5a357-db60-41b1-fc7b-08ddca2c5fc7', 'job_code': 'EnsureConnection'}
The webhooks can be decrypted exactly the same way.