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:
- Displayed once at creation time
- Hashed with bcrypt (cost 12) using a configurable salt before storage
- 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 Unauthorizedwith message"Invalid API key" - Invalid API key: Returns
401 Unauthorizedwith 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.
Server-Side Proxy Pattern (Recommended)
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
| Status | Meaning | Description |
|---|---|---|
| 401 | Unauthorized | Missing or invalid API key (also returned for non-existent form slugs to prevent enumeration) |
| 403 | Forbidden | Form is inactive or origin not allowed |
Related
- Security Overview — Full security architecture
- Anti-Abuse Protections — Rate limiting, Turnstile, origin validation
- Client Usage — Using the client library with authentication