Skip to content

API Reference

Comprehensive documentation of all Subscribe Flow API endpoints.

Interactive Documentation

Format URL Description
Swagger UI /docs Interactive API exploration with try-it-out
ReDoc /redoc Clear reference documentation
OpenAPI /openapi.json OpenAPI 3.1 Specification

Base URL

Environment URL
Production https://api.subscribeflow.net
Local http://localhost:8000

Authentication

Subscribe Flow supports dual authentication: API key OR session cookie. Every /api/v1/* endpoint accepts either method.

API Key (Programmatic Access)

All admin endpoints (/api/v1/*) accept API key authentication:

Bash
curl -X GET "http://localhost:8000/api/v1/subscribers" \
  -H "X-API-Key: sf_live_your_api_key"

API keys are organization-scoped. Each key belongs to exactly one organization.

Scopes:

Scope Description
subscribers:read Read subscribers
subscribers:write Create/update subscribers
subscribers:delete Delete subscribers
tags:read Read tags
tags:write Create/update/delete tags
webhooks:manage Manage webhook endpoints

Dashboard users authenticate via magic link, which sets an HTTP-only sf_session cookie. Organization context is provided via the X-Organization-Id header:

Bash
1
2
3
curl -X GET "http://localhost:8000/api/v1/subscribers" \
  -H "X-Organization-Id: 550e8400-e29b-41d4-a716-446655440000" \
  -b "sf_session=eyJhbGciOi..."

Token (Preference Center)

Preference Center endpoints use JWT tokens as query parameters:

Bash
curl "http://localhost:8000/preference-center?token=eyJhbGciOi..."

Auth API

Endpoints for magic link login, session management, and organization management. These endpoints do not require API key auth.

HTTP
POST /auth/magic-link

Request Body:

JSON
1
2
3
{
  "email": "user@example.com"
}

Response (200 OK):

JSON
1
2
3
{
  "message": "If the email exists, a magic link has been sent."
}

Anti-Enumeration

Always returns 200 regardless of whether the email exists.


Verifies the token and sets an HTTP-only session cookie (sf_session, 7 days).

HTTP
GET /auth/verify?token={magic_link_token}

Response (200 OK):

JSON
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "has_organizations": true,
  "organizations": [
    {
      "id": "660e8400-e29b-41d4-a716-446655440000",
      "name": "My Company",
      "slug": "my-company",
      "role": "owner"
    }
  ]
}

Errors:

Status Error Description
401 magic_link_expired Token has expired
401 magic_link_already_used Token was already used
401 invalid_token Token not found or invalid

Create Organization

Requires session auth (cookie). The calling user becomes the owner.

HTTP
POST /auth/organizations

Request Body:

JSON
1
2
3
4
{
  "name": "My Company",
  "slug": "my-company"
}

Response (200 OK):

JSON
1
2
3
4
5
{
  "id": "660e8400-e29b-41d4-a716-446655440000",
  "name": "My Company",
  "slug": "my-company"
}

List Organizations

Requires session auth. Returns all organizations the current user belongs to.

HTTP
GET /auth/organizations

Response (200 OK):

JSON
1
2
3
4
5
6
7
8
[
  {
    "id": "660e8400-e29b-41d4-a716-446655440000",
    "name": "My Company",
    "slug": "my-company",
    "role": "owner"
  }
]

Logout

Clears the session cookie.

HTTP
POST /auth/logout

Response (200 OK):

JSON
1
2
3
{
  "message": "Logged out successfully."
}

Subscribers API

Create Subscriber

Creates a new subscriber with optional tag subscriptions.

HTTP
POST /api/v1/subscribers

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
5
6
7
8
{
  "email": "user@example.com",
  "tags": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"],
  "metadata": {
    "source": "website",
    "campaign": "summer-2026"
  }
}

Response (201 Created):

JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "status": "active",
  "tags": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Product Updates",
      "slug": "product-updates"
    }
  ],
  "metadata": {
    "source": "website",
    "campaign": "summer-2026"
  },
  "created_at": "2026-02-03T10:30:00Z",
  "updated_at": "2026-02-03T10:30:00Z"
}

Errors:

Status Error Description
400 validation_error Invalid email address
402 limit_exceeded Subscriber limit reached for current plan
409 conflict Email already exists

List Subscribers

Lists subscribers with pagination and filtering.

HTTP
GET /api/v1/subscribers

Auth: API Key or Session

Query Parameters:

Parameter Type Default Description
cursor string - Cursor for the next page
limit integer 50 Max. results (1-100)
status string - Filter: active, unsubscribed, bounced, complained
tag string - Filter by tag ID
sort string created_at Sort by: created_at, updated_at, email
order string desc Direction: asc, desc

Response (200 OK):

JSON
{
  "items": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "email": "user@example.com",
      "status": "active",
      "tags": [],
      "created_at": "2026-02-03T10:30:00Z",
      "updated_at": "2026-02-03T10:30:00Z"
    }
  ],
  "total": 1234,
  "next_cursor": "eyJpZCI6IjU1MGU4NDAwLi4uIn0="
}

Response Headers:

Header Description
X-Total-Count Total number of subscribers
Link Pagination links (RFC 5988)

Get Subscriber

HTTP
GET /api/v1/subscribers/{subscriber_id}

Auth: API Key or Session


Get Subscriber by Email

HTTP
GET /api/v1/subscribers/by-email/{email}

Auth: API Key or Session


Update Subscriber

HTTP
PUT /api/v1/subscribers/{subscriber_id}

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
5
6
{
  "metadata": {
    "plan": "premium"
  },
  "status": "unsubscribed"
}

Response (200 OK): Updated subscriber


Delete Subscriber

Permanently deletes a subscriber.

HTTP
DELETE /api/v1/subscribers/{subscriber_id}

Auth: API Key or Session

Response: 204 No Content


Manage Subscriber Tags

Add Tags

HTTP
POST /api/v1/subscribers/{subscriber_id}/tags

Auth: API Key or Session

Request Body:

JSON
1
2
3
{
  "tag_ids": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"]
}

Remove Tag

HTTP
DELETE /api/v1/subscribers/{subscriber_id}/tags/{tag_id}

Auth: API Key or Session


Get Subscriber Tags

HTTP
GET /api/v1/subscribers/{subscriber_id}/tags

Auth: API Key or Session



Tags API

Create Tag

HTTP
POST /api/v1/tags

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
5
6
7
8
{
  "name": "Product Updates",
  "slug": "product-updates",
  "description": "Receive notifications about new features",
  "metadata": {
    "category": "product"
  }
}

Response (201 Created):

JSON
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Product Updates",
  "slug": "product-updates",
  "description": "Receive notifications about new features",
  "metadata": {
    "category": "product"
  },
  "subscriber_count": 0,
  "created_at": "2026-02-03T10:30:00Z",
  "updated_at": "2026-02-03T10:30:00Z"
}

List Tags

HTTP
GET /api/v1/tags

Auth: API Key or Session

Query Parameters:

Parameter Type Default Description
cursor string - Cursor for the next page
limit integer 50 Max. results (1-100)
sort string name Sort by: name, created_at, subscriber_count
order string asc Direction: asc, desc

Get Tag

HTTP
GET /api/v1/tags/{tag_id}

Auth: API Key or Session


Get Tag by Name

HTTP
GET /api/v1/tags/by-name/{name}

Auth: API Key or Session


Update Tag

HTTP
PUT /api/v1/tags/{tag_id}

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
{
  "name": "New Name",
  "description": "New description"
}

Delete Tag

HTTP
DELETE /api/v1/tags/{tag_id}

Auth: API Key or Session

Response: 204 No Content

Impact on Subscribers

Deleting a tag automatically removes all subscriber associations.


Templates API

Create Template

HTTP
POST /api/v1/templates

Auth: API Key or Session


List Templates

HTTP
GET /api/v1/templates

Auth: API Key or Session


Get Template

HTTP
GET /api/v1/templates/{template_id}

Auth: API Key or Session


Update Template

HTTP
PUT /api/v1/templates/{template_id}

Auth: API Key or Session


Delete Template

HTTP
DELETE /api/v1/templates/{template_id}

Auth: API Key or Session


Preview Template

HTTP
POST /api/v1/templates/{template_id}/preview

Auth: API Key or Session


Emails API

Send Email

HTTP
POST /api/v1/emails/send

Auth: API Key or Session

Errors:

Status Error Description
402 limit_exceeded Monthly email limit reached for current plan

Campaigns API

Create Campaign

HTTP
POST /api/v1/campaigns

Auth: API Key or Session


List Campaigns

HTTP
GET /api/v1/campaigns

Auth: API Key or Session


Get Campaign

HTTP
GET /api/v1/campaigns/{campaign_id}

Auth: API Key or Session


Update Campaign

HTTP
PUT /api/v1/campaigns/{campaign_id}

Auth: API Key or Session


Send Campaign

HTTP
POST /api/v1/campaigns/{campaign_id}/send

Auth: API Key or Session


Cancel Campaign

HTTP
POST /api/v1/campaigns/{campaign_id}/cancel

Auth: API Key or Session


Retry Campaign

HTTP
POST /api/v1/campaigns/{campaign_id}/retry

Auth: API Key or Session


Duplicate Campaign

HTTP
POST /api/v1/campaigns/{campaign_id}/duplicate

Auth: API Key or Session


Count Campaign Recipients

HTTP
POST /api/v1/campaigns/{campaign_id}/count-recipients

Auth: API Key or Session


Email Triggers API

Create Trigger

HTTP
POST /api/v1/email-triggers

Auth: API Key or Session


List Triggers

HTTP
GET /api/v1/email-triggers

Auth: API Key or Session


Get Trigger

HTTP
GET /api/v1/email-triggers/{trigger_id}

Auth: API Key or Session


Update Trigger

HTTP
PUT /api/v1/email-triggers/{trigger_id}

Auth: API Key or Session


Delete Trigger

HTTP
DELETE /api/v1/email-triggers/{trigger_id}

Auth: API Key or Session


Outgoing Webhooks API

Create Webhook Endpoint

HTTP
POST /api/v1/webhooks

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
5
6
7
8
9
{
  "url": "https://your-app.com/webhooks/subscribeflow",
  "events": [
    "subscriber.created",
    "subscriber.updated",
    "tag.subscribed"
  ],
  "description": "Main production webhook"
}

Response (201 Created):

JSON
{
  "id": "8fa85f64-5717-4562-b3fc-2c963f66afa6",
  "url": "https://your-app.com/webhooks/subscribeflow",
  "events": ["subscriber.created", "subscriber.updated", "tag.subscribed"],
  "description": "Main production webhook",
  "status": "active",
  "signing_secret": "whsec_abc123...",
  "created_at": "2026-02-03T10:30:00Z",
  "updated_at": "2026-02-03T10:30:00Z"
}

Signing Secret

The signing_secret is only returned upon creation. Store it securely for signature verification.

Available Events:

Event Description
subscriber.created New subscriber created
subscriber.updated Subscriber updated
subscriber.deleted Subscriber deleted
tag.subscribed Tag subscribed
tag.unsubscribed Tag unsubscribed
subscriber.note_added Note added to subscriber

List Webhooks

HTTP
GET /api/v1/webhooks

Auth: API Key or Session


Get Webhook

HTTP
GET /api/v1/webhooks/{webhook_id}

Auth: API Key or Session


Update Webhook

HTTP
PUT /api/v1/webhooks/{webhook_id}

Auth: API Key or Session


Delete Webhook

HTTP
DELETE /api/v1/webhooks/{webhook_id}

Auth: API Key or Session


Test Webhook

Sends a test event to the webhook endpoint.

HTTP
POST /api/v1/webhooks/{webhook_id}/test

Auth: API Key or Session

Response (200 OK):

JSON
1
2
3
4
5
{
  "success": true,
  "status_code": 200,
  "response_time_ms": 145
}

Regenerate Webhook Secret

HTTP
POST /api/v1/webhooks/{webhook_id}/regenerate-secret

Auth: API Key or Session


List Webhook Deliveries

HTTP
GET /api/v1/webhooks/{webhook_id}/deliveries

Auth: API Key or Session


Retry Webhook Delivery

HTTP
POST /api/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retry

Auth: API Key or Session


Get Webhook Stats

HTTP
GET /api/v1/webhooks/{webhook_id}/stats

Auth: API Key or Session


Get Global Webhook Stats

HTTP
GET /api/v1/webhooks/stats/global

Auth: API Key or Session


List Webhook Event Types

HTTP
GET /api/v1/webhooks/event-types

Auth: API Key or Session


Subscriber Notes API

Create Note

HTTP
POST /api/v1/subscribers/{subscriber_id}/notes

Auth: API Key or Session


List Notes

HTTP
GET /api/v1/subscribers/{subscriber_id}/notes

Auth: API Key or Session


Get Note

HTTP
GET /api/v1/subscribers/{subscriber_id}/notes/{note_id}

Auth: API Key or Session


Update Note

HTTP
PUT /api/v1/subscribers/{subscriber_id}/notes/{note_id}

Auth: API Key or Session


Delete Note

HTTP
DELETE /api/v1/subscribers/{subscriber_id}/notes/{note_id}

Auth: API Key or Session


API Keys API

List Available Scopes

HTTP
GET /api/v1/api-keys/scopes

Auth: API Key or Session


Create API Key

HTTP
POST /api/v1/api-keys

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
{
  "name": "Production Backend",
  "scopes": ["subscribers:read", "subscribers:write", "tags:read"]
}

Response (201 Created):

JSON
1
2
3
4
5
6
7
{
  "id": "9fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Production Backend",
  "key": "sf_live_abc123xyz...",
  "scopes": ["subscribers:read", "subscribers:write", "tags:read"],
  "created_at": "2026-02-03T10:30:00Z"
}

API Key Storage

The full API key is only displayed once upon creation. Store it securely -- it cannot be recovered.


List API Keys

HTTP
GET /api/v1/api-keys

Auth: API Key or Session


Get API Key

HTTP
GET /api/v1/api-keys/{api_key_id}

Auth: API Key or Session


Update API Key

HTTP
PATCH /api/v1/api-keys/{api_key_id}

Auth: API Key or Session


Rotate API Key

HTTP
POST /api/v1/api-keys/{api_key_id}/rotate

Auth: API Key or Session


Delete API Key

HTTP
DELETE /api/v1/api-keys/{api_key_id}

Auth: API Key or Session


Preference Center API

Public endpoints for subscriber self-service. These endpoints use token-based authentication (JWT in query parameter).

Get Preferences

HTTP
GET /preference-center?token={jwt_token}

Response:

JSON
{
  "email": "user@example.com",
  "subscribed_tags": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Product Updates",
      "slug": "product-updates",
      "description": "Receive notifications about new features",
      "subscribed": true,
      "subscribed_at": "2026-01-15T10:30:00Z"
    }
  ],
  "available_tags": [
    {
      "id": "7ca85f64-5717-4562-b3fc-2c963f66afa9",
      "name": "Weekly Digest",
      "slug": "weekly-digest",
      "description": "Weekly summary",
      "subscribed": false
    }
  ]
}

Subscribe to Tag

HTTP
PUT /preference-center/tags/{tag_id}?token={jwt_token}

Unsubscribe from Tag

HTTP
DELETE /preference-center/tags/{tag_id}?token={jwt_token}

Request Data Export (GDPR)

HTTP
GET /preference-center/data-export?token={jwt_token}

Request Account Deletion (GDPR)

HTTP
DELETE /preference-center/account?token={jwt_token}

Billing API

Endpoints for subscription management and Stripe integration.

Get Current Subscription

HTTP
GET /api/v1/billing/subscription

Auth: API Key or Session (Owner only)

Response (200 OK):

JSON
1
2
3
4
5
6
7
8
9
{
  "tier": "starter",
  "status": "active",
  "current_period_end": "2026-03-03T00:00:00Z",
  "max_subscribers": 5000,
  "max_emails_per_month": 10000,
  "emails_sent_this_month": 1234,
  "subscriber_count": 450
}

Create Checkout Session

Creates a Stripe Checkout session for upgrading the subscription.

HTTP
POST /api/v1/billing/create-checkout-session

Auth: API Key or Session (Owner only)

Request Body:

JSON
1
2
3
{
  "price_id": "price_xxx"
}

Response (200 OK):

JSON
1
2
3
{
  "checkout_url": "https://checkout.stripe.com/..."
}

Customer Portal

Opens the Stripe Customer Portal for managing payment methods and invoices.

HTTP
POST /api/v1/billing/customer-portal

Auth: API Key or Session (Owner only)

Response (200 OK):

JSON
1
2
3
{
  "portal_url": "https://billing.stripe.com/..."
}

Sync Subscription

Manually syncs the subscription status from Stripe.

HTTP
POST /api/v1/billing/sync-subscription

Auth: API Key or Session (Owner only)


Email Settings API

Endpoints for managing organization email sending configuration.

Get Email Settings

HTTP
GET /api/v1/settings/email

Auth: API Key or Session

Response (200 OK):

JSON
1
2
3
4
5
6
7
8
9
{
  "from_name": "My Company",
  "from_address": "noreply@mycompany.com",
  "effective_from": "My Company <noreply@mycompany.com>",
  "is_custom_domain": true,
  "custom_domain": "mycompany.com",
  "custom_domain_status": "verified",
  "can_add_custom_domain": false
}

Update Email Settings

Update from_name (all tiers) or from_address (requires verified custom domain).

HTTP
PUT /api/v1/settings/email

Auth: API Key or Session

Request Body:

JSON
1
2
3
4
{
  "from_name": "My Company",
  "from_address": "noreply@mycompany.com"
}

Add Custom Domain

Start domain verification via Resend API. Professional tier only.

HTTP
POST /api/v1/settings/email/domain

Auth: API Key or Session

Request Body:

JSON
1
2
3
{
  "domain": "mycompany.com"
}

Response (201 Created):

JSON
{
  "domain": "mycompany.com",
  "status": "pending",
  "dns_records": [
    {
      "type": "TXT",
      "name": "_resend.mycompany.com",
      "value": "v=spf1 ..."
    }
  ]
}

Check Domain Verification

HTTP
GET /api/v1/settings/email/domain

Auth: API Key or Session


Remove Custom Domain

HTTP
DELETE /api/v1/settings/email/domain

Auth: API Key or Session

Response: 204 No Content


Platform Admin API

Endpoints for platform-level administration. Only accessible by organizations with is_platform_admin = true (currently only Talent Factory GmbH).

All endpoints require the RequirePlatformAdmin dependency, which verifies the requesting organization has platform admin privileges.

List Organizations

HTTP
GET /api/v1/admin/organizations

Auth: API Key or Session (Platform Admin only)


Get Organization

HTTP
GET /api/v1/admin/organizations/{organization_id}

Auth: API Key or Session (Platform Admin only)


Update Organization

HTTP
PATCH /api/v1/admin/organizations/{organization_id}

Auth: API Key or Session (Platform Admin only)


Delete Organization

HTTP
DELETE /api/v1/admin/organizations/{organization_id}

Auth: API Key or Session (Platform Admin only)


List Users

HTTP
GET /api/v1/admin/users

Auth: API Key or Session (Platform Admin only)


Get User

HTTP
GET /api/v1/admin/users/{user_id}

Auth: API Key or Session (Platform Admin only)


List Members of Organization

HTTP
GET /api/v1/admin/organizations/{organization_id}/members

Auth: API Key or Session (Platform Admin only)


Add Member to Organization

HTTP
POST /api/v1/admin/organizations/{organization_id}/members

Auth: API Key or Session (Platform Admin only)


Remove Member from Organization

HTTP
DELETE /api/v1/admin/organizations/{organization_id}/members/{user_id}

Auth: API Key or Session (Platform Admin only)


Incoming Webhooks

Resend Webhook

Receives webhook events from Resend (contact and email events).

HTTP
POST /webhooks/resend

Auth: HMAC SHA-256 signature (X-Resend-Signature header)

No API key required. Signature-verified only.


Stripe Webhook

Receives webhook events from Stripe (checkout, subscription, payment events).

HTTP
POST /webhooks/stripe

Auth: Stripe webhook signature

No API key required. Signature-verified only.


Health Endpoints

Basic Health Check

HTTP
GET /health

Response (200 OK):

JSON
1
2
3
{
  "status": "healthy"
}

Readiness Check

Checks database connection and Redis.

HTTP
GET /ready

Response (200 OK):

JSON
1
2
3
4
5
6
7
{
  "status": "ready",
  "checks": {
    "database": "ok",
    "redis": "ok"
  }
}

Error Responses

All errors follow the RFC 7807 Problem Details format:

JSON
1
2
3
4
5
6
7
{
  "type": "https://subscribeflow.net/errors/not_found",
  "title": "Resource Not Found",
  "status": 404,
  "detail": "Subscriber with ID '550e8400...' not found",
  "instance": "/api/v1/subscribers/550e8400..."
}

See Error Codes Reference for the complete list.


SDKs

Python

Python
1
2
3
4
5
6
7
from subscribeflow import SubscribeFlowClient

async with SubscribeFlowClient(api_key="sf_live_xxx") as client:
    subscriber = await client.subscribers.create(
        email="user@example.com",
        tags=["product-updates"],
    )

TypeScript

TypeScript
import { SubscribeFlowClient } from '@subscribeflow/sdk';

const client = new SubscribeFlowClient({
  apiKey: 'sf_live_xxx',
});

const subscriber = await client.subscribers.create({
  email: 'user@example.com',
  tags: ['product-updates'],
});

See SDK Setup Guide for details.


Further Reading