API Documentation

Create encrypted secrets programmatically with our REST API. Zero-knowledge encryption — your secrets and passwords never reach our servers.

How Recipients Get the Unlock Password
The unlock password is never sent to our API. You must share it separately with the recipient through a secure channel (e.g., WhatsApp, Signal, in person). Our API only stores the encrypted blob — we cannot and do not handle password delivery.

Your responsibility: When you create a secret via API, you must share (1) the secret URL and (2) the unlock password with the recipient through two different channels for security.

Authentication
Use an API token to authenticate. Create tokens in your Dashboard. Send the token in the Authorization header:
Authorization: Bearer enigma_xxxxxxxxxxxxxxxx

Without a valid token, requests use anonymous rate limits and may require reCAPTCHA.

POST /api/messages — Create a Secret
Creates an encrypted secret. You must encrypt the content client-side before sending. We never accept plaintext.

Request Body

{
  "encryptedContent": "string (REQUIRED) - pre-encrypted v2 blob",
  "authToken": "string (REQUIRED) - 64 hex chars derived from the password",
  "name": "string (optional, max 100 chars, stored unencrypted)",
  "expiresAt": "ISO8601 in the future, or null (optional)",
  "maxAccess": integer 1-1000 or null (optional)
}

Response (200)

{
  "success": true,
  "message": "Message stored successfully",
  "data": {
    "messageId": "a8Kx9pQ2",
    "deleteToken": "xxx (only for anonymous secrets)"
  }
}

Example (cURL)

curl -X POST https://enigmacrypt.app/api/messages \
  -H "Authorization: Bearer enigma_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "encryptedContent": "v2.<salt>.<iv>.<ciphertext>",
    "authToken": "<64 hex chars>",
    "name": "API Secret",
    "expiresAt": null,
    "maxAccess": 1
  }'
Client-Side Encryption (Required)
You must encrypt your secret locally before sending it to the API. Our server only accepts pre-encrypted content.

Algorithm

  • master = PBKDF2-HMAC-SHA256(password, salt, 600,000 iterations, 32 bytes)
  • encryption key = HMAC-SHA256(master, "enigmacrypt-v2-enc")
  • authToken = hex(HMAC-SHA256(master, "enigmacrypt-v2-auth"))
  • AES-256-GCM with a random 16-byte salt and a random 12-byte IV

Format

v2.<salt hex>.<iv hex>.<base64(ciphertext + 16-byte GCM tag)>

Message object before encryption: { content: "your-secret", created: "ISO8601" }

The server stores only a hash of authToken and releases the ciphertext to whoever presents it, which is how view limits are enforced without the server being able to decrypt. The token cannot be turned back into the encryption key.

Passwordless links: use a random 32-character hex string as the password and append it to the URL as a fragment (/s/<messageId>#<key>). The fragment is never sent to the server. The legacy v1 format (AES-CBC) is still accepted but deprecated: its view limits cannot be enforced by the server.

See integration examples below for complete code in Node.js, Python, Go, and cURL.

Ejemplos de integración
Código completo para cifrar y enviar secretos a la API en distintos lenguajes.
// No dependencies: uses Node's built-in crypto const crypto = require("crypto") function encryptSecret(secret, password) { const salt = crypto.randomBytes(16) const master = crypto.pbkdf2Sync(password, salt, 600000, 32, "sha256") const encKey = crypto.createHmac("sha256", master).update("enigmacrypt-v2-enc").digest() const authToken = crypto.createHmac("sha256", master).update("enigmacrypt-v2-auth").digest("hex") const iv = crypto.randomBytes(12) const cipher = crypto.createCipheriv("aes-256-gcm", encKey, iv) const payload = JSON.stringify({ content: secret, created: new Date().toISOString() }) const ciphertext = Buffer.concat([cipher.update(payload, "utf8"), cipher.final(), cipher.getAuthTag()]) return { encryptedContent: `v2.${salt.toString("hex")}.${iv.toString("hex")}.${ciphertext.toString("base64")}`, authToken, } } async function createSecret(secret, password, apiToken) { const { encryptedContent, authToken } = encryptSecret(secret, password) const res = await fetch("https://enigmacrypt.app/api/messages", { method: "POST", headers: { "Authorization": `Bearer ${apiToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ encryptedContent, authToken, name: "API Secret", expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), maxAccess: 1, }), }) const data = await res.json() if (!data.success) throw new Error(data.error) return { url: `https://enigmacrypt.app/s/${data.data.messageId}`, password } }
Rate Limits
Limits are per identifier (IP or API token). Exceeding returns 429.
  • Anonymous (no token): 20 requests per 15 minutes
  • With API token: 100 requests per 15 minutes