API Documentation

Access your encrypted files programmatically using our REST API. All file content remains encrypted - you'll need your encryption key to decrypt files after downloading.

Authentication

All API requests require your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Endpoints

List Files

GET/api/files

Returns all files associated with your API key.

Example Request:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://geohavnvault.com/api/files

Example Response:

{
  "success": true,
  "upload": {
    "id": "abc123",
    "status": "paid",
    "expiresAt": "2025-03-05T00:00:00Z",
    "fileCount": 2
  },
  "files": [
    {
      "id": "file123",
      "name": "encrypted-filename.pdf",
      "size": 1048576,
      "uploadDate": "2025-09-05T10:00:00Z",
      "encryptedUrl": "https://geohavnvault.com/api/download/file123"
    }
  ]
}

Get File Metadata

GET/api/files/{fileId}

Returns metadata for a specific file without downloading it.

Example Request:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://geohavnvault.com/api/files/file123

Download Encrypted File

GET/api/download/{fileId}

Downloads the encrypted file content. The file remains encrypted - you'll need to decrypt it using your encryption key.

Example Request:

# Using Authorization header
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://geohavnvault.com/api/download/file123 \
  -o encrypted-file.bin

# Using query parameter
curl "https://geohavnvault.com/api/download/file123?key=YOUR_API_KEY" \
  -o encrypted-file.bin

Response Headers:

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="encrypted-filename.pdf"
X-File-Encrypted: true

Decryption Examples

After downloading files via the API, you'll need to decrypt them using your encryption key. Files are encrypted using AES-256-GCM with the IV prepended to the encrypted data.

TypeScript / Node.js

import { createDecipheriv, createHash } from 'crypto';
import { readFile, writeFile } from 'fs/promises';

async function decryptFile(
  encryptedPath: string,
  decryptedPath: string,
  encryptionKey: string
) {
  // Read encrypted file
  const encryptedData = await readFile(encryptedPath);
  
  // Extract IV (first 16 bytes) and encrypted content
  const iv = encryptedData.slice(0, 16);
  const authTag = encryptedData.slice(16, 32);
  const encrypted = encryptedData.slice(32);
  
  // Derive key from string (must match frontend logic)
  const key = createHash('sha256')
    .update(encryptionKey)
    .digest();
  
  // Create decipher
  const decipher = createDecipheriv('aes-256-gcm', key, iv);
  decipher.setAuthTag(authTag);
  
  // Decrypt
  const decrypted = Buffer.concat([
    decipher.update(encrypted),
    decipher.final()
  ]);
  
  // Write decrypted file
  await writeFile(decryptedPath, decrypted);
}

// Usage
await decryptFile(
  'downloaded-file.bin',
  'decrypted-file.pdf',
  'your-encryption-key-here'
);

Python

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import hashlib

def decrypt_file(encrypted_path, decrypted_path, encryption_key):
    # Read encrypted file
    with open(encrypted_path, 'rb') as f:
        encrypted_data = f.read()
    
    # Extract IV (first 16 bytes) and auth tag
    iv = encrypted_data[:16]
    auth_tag = encrypted_data[16:32]
    ciphertext = encrypted_data[32:]
    
    # Derive key from string (must match frontend logic)
    key = hashlib.sha256(encryption_key.encode()).digest()
    
    # Create cipher
    cipher = Cipher(
        algorithms.AES(key),
        modes.GCM(iv, auth_tag),
        backend=default_backend()
    )
    decryptor = cipher.decryptor()
    
    # Decrypt
    decrypted = decryptor.update(ciphertext) + decryptor.finalize()
    
    # Write decrypted file
    with open(decrypted_path, 'wb') as f:
        f.write(decrypted)

# Usage
decrypt_file(
    'downloaded-file.bin',
    'decrypted-file.pdf',
    'your-encryption-key-here'
)

Rate Limits

EndpointRate Limit
/api/files20 requests per minute
/api/download/*30 requests per minute

Rate limits return a 429 status code with a Retry-After header indicating when you can retry.

Error Responses

All errors return JSON with an error message:

{
  "error": "Error message",
  "details": "Additional information if available"
}

Common Status Codes:

  • 401 - Missing or invalid API key
  • 403 - File has expired
  • 404 - File not found
  • 429 - Rate limit exceeded
  • 500 - Server error