Authentication¶
Subscribe Flow supports two authentication methods: API keys for programmatic access and magic links for dashboard login. All /api/v1/* endpoints accept either method (dual auth).
API Keys (Programmatic Access)¶
API keys are the recommended method for server-to-server communication and SDK usage. Each API key is scoped to exactly one organization.
Key Types¶
| Type | Prefix | Usage |
|---|---|---|
| Live | sf_live_ | Production |
| Test | sf_test_ | Development & Testing |
Creating an API Key¶
- Navigate to Settings > API Keys
- Click Create API Key
- Assign a name and select permissions
- Copy the key (it is only shown once!)
| Bash | |
|---|---|
Usage¶
The organization context is resolved automatically from the API key. No additional headers needed.
Scopes¶
| Scope | Description |
|---|---|
subscribers:read | Read subscribers |
subscribers:write | Create/modify subscribers |
subscribers:delete | Delete subscribers |
tags:read | Read tags |
tags:write | Create/modify tags |
webhooks:manage | Manage webhooks |
* | All permissions (admin) |
Security Best Practices
- Never use API keys in frontend/client-side code
- Store keys in environment variables
- Rotate keys regularly (every 90 days recommended)
- Grant minimal permissions (principle of least privilege)
Magic Links (Dashboard Access)¶
Magic links provide passwordless authentication for the admin dashboard. No passwords are stored or managed.
How It Works¶
sequenceDiagram
participant U as User
participant D as Dashboard
participant API as Subscribe Flow API
participant E as Email (Resend)
U->>D: Enter email on login page
D->>API: POST /auth/magic-link {email}
API->>E: Send magic link email
API->>D: 200 OK "Check your email"
E->>U: Email with login link
U->>U: Click link in email
U->>API: GET /auth/verify?token=xxx
API->>API: Validate token (not expired, not used)
API->>API: Create/find user, set session cookie
API-->>U: Set-Cookie: sf_session (HttpOnly, 7 days)
API->>D: Redirect to dashboard Step-by-Step Flow¶
-
Request magic link: User enters their email at the login page
Always returns200 OKregardless of whether the email exists (prevents email enumeration).Der optionale
locale-Parameter (deoderen, Standard:en) steuert die Sprache des Magic-Link-E-Mail-Inhalts (Betreff, Text, Button). -
Receive email: User receives an email containing a unique, time-limited login link.
-
Click link: The link calls the verify endpoint with the token:
Text Only -
Session established: On success, the API:
- Verifies the token is valid, not expired, not already used
- Creates the user account if it does not exist yet
- Sets an HTTP-only session cookie (
sf_session) valid for 7 days - Returns the user's organizations
-
Use the dashboard: Subsequent requests include the session cookie automatically. The dashboard sends an
X-Organization-Idheader to specify which organization the user is working with.
Session Properties¶
| Property | Value |
|---|---|
| Cookie name | sf_session |
| Format | JWT (signed with app secret) |
| HttpOnly | Yes |
| SameSite | Lax |
| Secure | Yes (production only) |
| Lifetime | 7 days |
Organization Context¶
When using session auth, requests must include the organization context:
| Bash | |
|---|---|
The API verifies that the user is a member of the specified organization before processing the request.
Logout¶
| Bash | |
|---|---|
Clears the session cookie.
Dual Auth¶
All /api/v1/* endpoints accept either API key or session cookie:
| Method | Header/Cookie | Org Resolution |
|---|---|---|
| API Key | X-API-Key: sf_live_xxx | From API key record |
| Session | Cookie sf_session + Header X-Organization-Id | From header, verified via membership |
This means the same endpoints work for both SDK integrations and the admin dashboard.
Technisches Detail: Wenn eine Session Cookie vorhanden ist, gibt die get_api_key() Dependency None zurück statt 401 Unauthorized. Dadurch können alle require_scope Dependencies den Request über Session Auth autorisieren. Ohne diesen Mechanismus würden Dashboard-Requests (Session Cookie, kein API Key) an scope-geschützten Endpoints fehlschlagen.
Roles¶
Each user has a role within an organization:
| Role | Access |
|---|---|
| Owner | Full access: subscribers, tags, billing, settings, API keys |
| Member | Standard access: subscribers, tags, templates, campaigns |
Owner-only features:
- Billing management (
/api/v1/billing/*) - Email settings (
/api/v1/settings/email) - Organization settings
Preference Center Tokens¶
The Preference Center uses separate JWT tokens for subscriber self-service. These are not related to dashboard authentication.
| Bash | |
|---|---|
Tokens are scoped to a single subscriber and grant access only to that subscriber's preference data.
Security Recommendations¶
API Keys¶
- Never in client code: API keys belong on the server only
- Environment variables:
export SUBSCRIBEFLOW_API_KEY=sf_live_xxx - Regular rotation: At least every 90 days
- Audit logs: Monitor key usage
Magic Link Sessions¶
- Token security: Magic links are single-use and expire after 15 minutes
- Anti-enumeration: The magic link endpoint always returns success
- Cookie security: HttpOnly + SameSite=Lax + Secure prevents common web attacks
- Session expiry: 7-day lifetime limits exposure window
General¶
| Python | |
|---|---|
Troubleshooting¶
401 Unauthorized
- Check the X-API-Key header (not Authorization: Bearer)
- Has the key/token expired?
- Is the key prefix correct (live vs. test)?
- For session auth: is the sf_session cookie present?
403 Forbidden
- Does the key have the required scopes?
- Does the resource belong to the same organization?
- For session auth: is X-Organization-Id set correctly?
- Is the user a member of the organization?
- Are you accessing an owner-only endpoint as a member?
Magic link not received
- Check spam/junk folder
- The API always returns 200 (anti-enumeration), so the email may not exist
- Verify Resend is configured correctly
- Check server logs for email delivery errors