Security
How MakeEmWait protects your data and your users' data.
MakeEmWait implements defense-in-depth security across multiple layers — authentication, data protection, bot filtering, and network controls. This page covers the security measures that protect both your account and your users’ data.
Authentication
MakeEmWait supports two authentication methods: JWT tokens for browser-based sessions and API keys for programmatic access.
JWT Authentication
MakeEmWait uses a dedicated authentication service for user management. When you log in, the auth service returns an ID token (JWT) signed with RSA256. Every authenticated API request is verified server-side, validating the token’s signature, expiry, audience, and issuer.
Tokens can be passed in two ways:
| Method | Format |
|---|---|
| Authorization header | Authorization: Bearer <token> |
| Query parameter | ?token=<token> |
Tokens expire after 1 hour. After expiry, you must re-authenticate to get a new token.
API Key Authentication
Every account gets a personal API key for programmatic access. API keys use the format mew_live_ followed by 24 random bytes, giving 192 bits of entropy.
Keys are SHA-256 hashed before storage. The raw key is never stored in the database – it is shown only once at creation or regeneration and cannot be retrieved later. If you lose your key, you must regenerate a new one.
When you authenticate with an API key, MakeEmWait hashes the key you provide and looks up the matching hash in the database. If a match is found, the request is authenticated as that user.
Regeneration requires JWT authentication. You cannot regenerate an API key using the API key itself. This is a deliberate security measure – if a key is compromised, an attacker cannot use it to issue a replacement key and lock you out.
Admin API Key
A separate admin API key is used for internal operations. This key is compared using timing-safe comparison (crypto.timingSafeEqual) to prevent timing attacks, where an attacker could measure response times to guess the key character by character.
Bot Protection
Public signup forms include a honeypot field – a hidden form field named website that is invisible to real users via CSS but visible to automated bots that fill in every field.
When a bot fills in the honeypot field, the API returns a fake 201 response with fabricated signup data (a fake position, a fake referral token). The bot believes the signup succeeded, but no record is actually created in the database.
This approach blocks automated form submissions without requiring CAPTCHAs or any user interaction.
reCAPTCHA v3 Bot Protection
In addition to the honeypot field, you can enable Google reCAPTCHA v3 for invisible bot scoring on your signup forms. reCAPTCHA v3 runs in the background without any user interaction and assigns each visitor a score from 0.0 (likely bot) to 1.0 (likely human).
Enabling reCAPTCHA
-
Get a reCAPTCHA v3 site key from the Google reCAPTCHA admin console. Choose “reCAPTCHA v3” and add your domain(s).
-
Get your secret key from the same reCAPTCHA admin console (different from the site key — the secret key stays on the server).
-
Enable it on your waitlist:
PATCH /waitlists/{waitlist_id}
{
"settings": {
"captcha_enabled": true,
"captcha_site_key": "your-recaptcha-v3-site-key",
"captcha_secret_key": "your-recaptcha-v3-secret-key",
"captcha_threshold": 0.5
}
}
| Setting | Type | Description |
|---|---|---|
captcha_enabled |
boolean | Enable or disable reCAPTCHA v3 |
captcha_site_key |
string (max 100 chars) | Your reCAPTCHA v3 site key (public, embedded in the form) |
captcha_secret_key |
string (max 100 chars) | Your reCAPTCHA v3 secret key (private, used server-side to verify tokens) |
captcha_threshold |
number (0-1) | Minimum score required to pass (default: 0.5). Scores below this are rejected. Higher values are stricter. |
How It Works
When reCAPTCHA is enabled:
- The hosted signup page and embed widget automatically load the reCAPTCHA v3 script using your site key
- When a user submits the form, a reCAPTCHA token is generated and included in the signup request as
captcha_token - The server verifies the token with Google using your secret key and checks the score against your threshold
- Low-scoring submissions (likely bots) are rejected
reCAPTCHA works alongside the existing honeypot protection. The honeypot catches simple bots that fill in hidden fields, while reCAPTCHA catches more sophisticated automated submissions. Both protections can be active at the same time.
Available on all plans.
SSRF Protection
All webhook URLs – custom webhooks, Slack incoming webhooks, and Discord webhooks – are validated when saved to your waitlist settings. Two rules are enforced:
- HTTPS required – only
https://URLs are accepted - Private IP ranges blocked – requests to internal networks are rejected to prevent Server-Side Request Forgery (SSRF)
The following addresses and ranges are blocked:
localhost,127.0.0.1,::110.0.0.0/8,172.16.0.0/12,192.168.0.0/16169.254.0.0/16(link-local, including cloud metadata endpoints)
CORS Policy
Cross-Origin Resource Sharing (CORS) headers are set in server middleware on every response.
| Endpoint type | CORS policy |
|---|---|
Public endpoints (/public/*) |
Wildcard Access-Control-Allow-Origin: * – allows the embed widget to work on any domain |
| Authenticated endpoints | Restrictive CORS headers |
This split ensures the embeddable signup widget works from any origin while keeping authenticated API routes locked down.
Domain Restrictions
Waitlist owners can restrict which email addresses are allowed to sign up:
- Allowed domains – limit signups to specific email domains (e.g., only
@yourcompany.com) - Exclude personal emails – block personal email providers like Gmail, Yahoo, Hotmail, and others with the
exclude_personal_emailssetting
These restrictions are useful when you want to ensure only business or corporate email addresses can join your waitlist.
Data Residency & Encryption
| Aspect | Detail |
|---|---|
| Region | United States (Oregon) |
| Encryption at rest | AES-256 encryption on all stored data |
| Encryption in transit | TLS 1.2+ enforced on all API endpoints |
| Data retention | Data persists until explicitly deleted by the user or account owner |
There is no automatic data expiration or audit log retention policy. All data remains in the database until a user deletes their signup, a waitlist owner deletes a record, or an account is deleted.
Data Protection
MakeEmWait follows a principle of minimizing what it handles directly:
| Data type | Handled by |
|---|---|
| Passwords | Dedicated authentication service – never stored in application code or the database |
| Payment data | Stripe – no credit card numbers touch MakeEmWait servers |
| Email delivery | Resend – no email infrastructure to secure |
| API communication | HTTPS enforced on all endpoints |
Data Deletion
MakeEmWait provides three levels of data removal:
Unsubscribe from Emails
Signups can opt out of marketing emails (blasts) via a personalized unsubscribe link included in every email. The link contains an HMAC-SHA256 token that is verified server-side before processing the request. This prevents anyone from unsubscribing other people by guessing URLs.
Delete My Data
Signups can request full deletion of their record from the waitlist. This uses a separate endpoint with its own HMAC token – distinct from the unsubscribe token – so that one token type cannot be used for the other operation.
Account Deletion
Account owners can delete their entire account. This cascades to remove:
- All waitlists owned by the account
- All signups across those waitlists
- The Stripe customer record
- The authentication service user record
Token Security
All sensitive tokens use protections against forgery and timing attacks:
| Token type | Protection |
|---|---|
| Email verification | Timing-safe comparison (crypto.timingSafeEqual) |
| Unsubscribe | HMAC-SHA256 signature verification |
| Delete my data | HMAC-SHA256 with a different prefix than unsubscribe tokens |
| Admin API key | Timing-safe comparison (crypto.timingSafeEqual) |
The use of different HMAC prefixes for unsubscribe and delete-my-data tokens ensures that a token generated for one operation cannot be reused for the other, even though both are signed with the same secret key.
Related
- Account Settings — manage API keys and password
- Rate Limits — API rate limiting and abuse protection
- Error Handling — security-related error codes