Data Security and Decryption
You may opt to receive your export encrypted at rest. Along with your API key, we’ll issue a base64-encoded 32-byte AES key (“Customer Encryption Key”).
Important: Store your key in a secrets manager. Never share it with us.
Encrypted delivery format
To request encryption:
{
"compression": "zip",
"encrypted": true
}
You’ll receive:
- A ZIP from a presigned S3 URL
- Inside, a single
.ndjsonfile - Each line contains:
{"encrypted_data":"key_id:iv_b64:ciphertext_plus_tag_b64"}
Part | Meaning |
| Identifier of the key used (informational) |
| 12-byte IV (nonce), base64 |
| Base64 of `ciphertext |
Example (shortened):
{"encrypted_data":"3e9c..e2f1:0R0y8kS3g8m2s6v8:AAABBBCCC...zzz"}
Cryptography profile
- Cipher: AES-256-GCM
- Key: your 32-byte key (base64-decode first)
- IV: 12 bytes (from
iv_b64) - Auth tag: 16 bytes (trailing bytes of the decoded blob)
- AAD:
stream:{customer_id}:{row_index} row_indexis zero-based for each NDJSON line.
Decryption steps
- Unzip to get
job_id.ndjson. - Read file line by line.
- Parse JSON; get
encrypted_data. split(":") → [key_id, iv_b64, ctext_tag_b64].iv = base64(iv_b64).raw = base64(ctext_tag_b64); then:ciphertext = raw[0 : len(raw) - 16]tag = raw[len(raw) - 16 : ]aad = "stream:{customer_id}:{row_index}".- Decrypt with AES-256-GCM (
key,iv,aad,ciphertext,tag). - Parse the UTF-8 JSON result.
Reference implementations
Replace {{customer_key_b64}} and {{customer_id}} with your values (or Theneo variables).
Python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64
import json
# Your pre-shared values
derived_key_base64 = "your-pre-shared-key"
customer_id = "your-customer-id"
# Decode your key
encryption_key = base64.b64decode(derived_key_base64)
# For each encrypted line (row_index starts at 0)
def decrypt_line(encrypted_data, row_index):
# Split the encrypted data
parts = encrypted_data.split(":")
iv = base64.b64decode(parts[1])
ciphertext = base64.b64decode(parts[2])
# Create AAD
aad = f"stream:{customer_id}:{row_index}".encode('utf-8')
# Decrypt
aesgcm = AESGCM(encryption_key)
decrypted_bytes = aesgcm.decrypt(iv, ciphertext, aad)
# Parse JSON
return json.loads(decrypted_bytes.decode('utf-8'))Node.js
const crypto = require("node:crypto");
const CUSTOMER_KEY_B64 = "{{customer_key_b64}}";
const CUSTOMER_ID = "{{customer_id}}";
const KEY = Buffer.from(CUSTOMER_KEY_B64, "base64");
function decryptLine(encryptedData, rowIndex) {
const [keyId, iv_b64, ctext_tag_b64] = encryptedData.split(":");
const iv = Buffer.from(iv_b64, "base64");
const raw = Buffer.from(ctext_tag_b64, "base64");
const ciphertext = raw.slice(0, -16);
const tag = raw.slice(-16);
const aad = Buffer.from(`stream:${CUSTOMER_ID}:${rowIndex}`, "utf8");
const decipher = crypto.createDecipheriv("aes-256-gcm", KEY, iv);
decipher.setAAD(aad);
decipher.setAuthTag(tag);
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
return JSON.parse(decrypted.toString("utf8"));
}Java
C# (.NET)
using System;
using System.Text;
using System.Text.Json;
using System.Security.Cryptography;
public static class Decrypter
{
private static readonly string CustomerKeyB64 = "{{customer_key_b64}}";
private static readonly string CustomerId = "{{customer_id}}";
private static readonly byte[] Key = Convert.FromBase64String(CustomerKeyB64);
public static string DecryptLine(string encryptedData, int rowIndex)
{
var parts = encryptedData.Split(':');
var iv = Convert.FromBase64String(parts[1]);
var raw = Convert.FromBase64String(parts[2]);
var ciphertext = raw[..^16];
var tag = raw[^16..];
var aad = Encoding.UTF8.GetBytes($"stream:{CustomerId}:{rowIndex}");
var plaintext = new byte[ciphertext.Length];
using var aes = new AesGcm(Key);
aes.Decrypt(iv, ciphertext, tag, plaintext, aad);
return Encoding.UTF8.GetString(plaintext);
}
}Validation & troubleshooting
- Key decodes to exactly 32 bytes
- AAD exactly
stream:{customer_id}:{row_index}(row index is zero-based) - IV is 12 bytes; tag is the last 16 bytes of the decoded blob
- Don’t reuse IV/AAD on your side when testing
What made this section unhelpful for you?
On this page
- Data Security and Decryption