API Documentation

Integrate Burn the Secret into your applications to create secure, self-destructing secret links programmatically.

Base URL

https://burnthesecret.com/api

AES-256-GCM Encryption

Industry-standard authenticated encryption used by banks and governments.

Zero-Knowledge Architecture

Decryption keys never touch our servers. Your secrets stay secret.

Self-Destructing Links

Set view limits and expiration times. Secrets auto-delete after access.

RESTful JSON API

Simple HTTP endpoints with consistent response formats.

Authentication

All API requests require authentication using an API key. Include your key in the Authorization header:

Authorization: Bearer ks_live_your_api_key

Create API keys in your Dashboard → API Access. Requests without a valid API key receive a 401 Unauthorized response.

Quick Start

Create a secret link with a single API call:

curl -X POST https://burnthesecret.com/api/secrets \
  -H "Authorization: Bearer ks_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "my-sensitive-password", "ttl": 86400, "maxViews": 1}'

Endpoints

POST/secretsAuth Required

Create a new secret link.

Request Body

ParameterTypeDescription
textstring | objectSecret content (max 50MB). Plain string or {ciphertext, iv}
ttlnumberTime to live in seconds. 1800-604800. Default: 86400
maxViewsnumber | nullMax views (1-100) or null for unlimited. Default: 1
passphrasestringOptional passphrase protection (min 4 chars)
filesarrayArray of files with filename, mimeType, data

Response

{
  "success": true,
  "secretUrl": "https://burnthesecret.com/secret/Kj8mNp2x...#key=...",
  "metadataUrl": "https://burnthesecret.com/receipt/Yz3nLq9b...",
  "metadata": {
    "key": "Yz3nLq9bFs7hGc...",
    "secretKey": "Kj8mNp2xQr5tVw...",
    "expiresAt": "2026-01-27T12:00:00.000Z",
    "maxViews": 1
  }
}
GET/secrets/:key

Check if a secret exists and get metadata. Does not consume a view.

{
  "exists": true,
  "type": "secret",
  "hasPassphrase": false,
  "expiresAt": "2026-01-27T12:00:00.000Z",
  "maxViews": 1,
  "viewsRemaining": 1
}
POST/secrets/:secretKey

Retrieve secret content. This consumes one view.

Request Body (if passphrase protected)

{ "passphrase": "your-passphrase" }

Response

{
  "success": true,
  "text": {
    "ciphertext": "base64-encoded...",
    "iv": "base64-encoded..."
  },
  "viewsRemaining": 0
}

Decrypt using the key from the URL fragment with AES-256-GCM.

DELETE/secrets/:key

Permanently delete (burn) a secret.

curl -X DELETE https://burnthesecret.com/api/secrets/Yz3nLq9bFs7hGc...

Encryption

Burn the Secret uses AES-256-GCM, the same encryption used by TLS 1.3, Signal, and government systems.

Zero-Knowledge Architecture

The decryption key is stored in the URL fragment (after #), which browsers never send to servers. We only store encrypted ciphertext—useless without the key. Decryption happens entirely in the recipient's browser.

URL Structure:

https://burnthesecret.com/secret/secretKey#key=decryptionKey

Server-Side Encryption

Send plaintext—we encrypt it and return the URL with the key in the fragment.

Client-Side Encryption

Encrypt before sending—you control the key. Maximum security.

Rate Limits

1,000 requests per hour per API key using a sliding-window algorithm.

Response Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1706360400

Errors

All errors return a consistent JSON structure:

{
  "success": false,
  "error": "INVALID_PASSPHRASE",
  "message": "Invalid passphrase",
  "hint": "Include the correct passphrase in your request.",
  "requestId": "req_abc123..."
}
StatusCodeDescription
200Success
400INVALID_*Bad request (invalid params)
401UNAUTHORIZEDMissing/invalid API key
403INVALID_PASSPHRASEWrong passphrase
404NOT_FOUNDSecret not found
410SECRET_EXPIREDExpired/burned/max views
429TOO_MANY_ATTEMPTSRate limit exceeded
500INTERNAL_ERRORServer error

For security, 404 is returned for any inaccessible secret regardless of reason.

Security Best Practices

Choose the Right Encryption Mode

  • Server-side: Convenient—we handle encryption. Plaintext briefly in memory.
  • Client-side: Maximum security—you control the key entirely.

Passphrase Tips

Use 8+ characters with mixed case, numbers, symbols. Share passphrases via a different channel than the URL.

Minimize Exposure

Use the shortest TTL and lowest maxViews needed. For one-time credentials, use maxViews: 1.

Code Examples

Decrypting Retrieved Secrets

JavaScript

const urlKey = window.location.hash.split('key=')[1];
const rawKey = Uint8Array.from(
  atob(urlKey.replace(/-/g, '+').replace(/_/g, '/')),
  c => c.charCodeAt(0)
);

const key = await crypto.subtle.importKey(
  'raw', rawKey, { name: 'AES-GCM' }, false, ['decrypt']
);

const ciphertext = Uint8Array.from(atob(response.text.ciphertext), c => c.charCodeAt(0));
const iv = Uint8Array.from(atob(response.text.iv), c => c.charCodeAt(0));

const plaintext = await crypto.subtle.decrypt(
  { name: 'AES-GCM', iv }, key, ciphertext
);
const secret = new TextDecoder().decode(plaintext);

Python

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64

key = base64.urlsafe_b64decode(url_key + '==')
ciphertext = base64.b64decode(response['text']['ciphertext'])
iv = base64.b64decode(response['text']['iv'])

aesgcm = AESGCM(key)
plaintext = aesgcm.decrypt(iv, ciphertext, None)
secret = plaintext.decode('utf-8')

Client-Side Encryption

For maximum security, encrypt before sending to Burn the Secret:

JavaScript

// Generate key and IV
const key = await crypto.subtle.generateKey(
  { name: 'AES-GCM', length: 256 }, true, ['encrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));

// Encrypt
const plaintext = new TextEncoder().encode('my-secret');
const ciphertext = await crypto.subtle.encrypt(
  { name: 'AES-GCM', iv }, key, plaintext
);

// Export key for URL
const rawKey = await crypto.subtle.exportKey('raw', key);
const keyBase64 = btoa(String.fromCharCode(...new Uint8Array(rawKey)));

// Send to API
const response = await fetch('https://burnthesecret.com/api/secrets', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ks_live_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: {
      ciphertext: btoa(String.fromCharCode(...new Uint8Array(ciphertext))),
      iv: btoa(String.fromCharCode(...iv))
    }
  })
});

const data = await response.json();
const fullUrl = data.secretUrl + '#key=' + keyBase64;