Authentication

Authentication

Authentication

Formic uses API key authentication for all form submissions. Every request to the Formic API (except health checks) must include a valid API key.

How It Works

API Key Header

API keys are sent via the X-API-Key HTTP header:

curl -X POST https://api.audelabs.com/v1/f/contact \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "message": "Hello"}'

Key Generation

When you create a form, Formic generates a random 32-character alphanumeric API key. The key is:

  1. Displayed once at creation time
  2. Hashed with bcrypt (cost 12) using a configurable salt before storage
  3. Never stored in plaintext — only the bcrypt hash is saved in the database

Key Rotation

You can rotate a form’s API key using the Formic CLI:

formic form rotate-key contact

This generates a new key and re-hashes it. The old key is immediately invalidated.

Anti-Enumeration Protection

Formic protects against form-slug enumeration attacks — attempts to discover valid form slugs by observing different error responses:

  • Non-existent form slug: Returns 401 Unauthorized with message "Invalid API key"
  • Invalid API key: Returns 401 Unauthorized with message "Invalid API key"

Both cases return the identical response, making it impossible for an attacker to distinguish between invalid keys and non-existent forms. A dummy bcrypt hash is verified in both cases to ensure constant-time response.

Never expose your API key in client-side (browser) code. Instead, use the server-side proxy pattern:

Browser (no API key)

      ▼  POST /api/submit
┌──────────────┐
│  Your Server  │  ← API key configured via environment variable
│  (Proxy)      │
└──────┬───────┘
       │  X-API-Key header (added by your server)

┌──────────────┐
│  Formic API   │
└──────────────┘

The Formic client library provides framework adapters for this pattern:

// Astro - src/pages/api/submit.ts
import { handleFormicRequest } from '@audelabs/formic-client/adapters/astro';

export const POST = handleFormicRequest({
  apiKey: import.meta.env.FORMIC_API_KEY,  // from .env, never client-side
  slug: 'contact-form'
});
// Next.js (App Router) - app/api/contact/route.ts
import { handleFormicRequest } from '@audelabs/formic-client/adapters/next';

export const POST = handleFormicRequest({
  apiKey: process.env.FORMIC_API_KEY!,  // from environment
  slug: 'contact-form'
}).POST;

Environment Variable Best Practices

Always store your API key in environment variables — never hardcode it or commit it to version control:

# .env (never commit to git!)
FORMIC_API_KEY=sk_live_abc123...

Add .env to your .gitignore:

# .gitignore
.env
.env.local

Error Responses

StatusMeaningDescription
401UnauthorizedMissing or invalid API key (also returned for non-existent form slugs to prevent enumeration)
403ForbiddenForm is inactive or origin not allowed